<?php

namespace Gedmo\Tree\Strategy\ODM\MongoDB;

use Doctrine\Persistence\ObjectManager;
use Gedmo\Mapping\Event\AdapterInterface;
use Gedmo\Tool\Wrapper\AbstractWrapper;
use Gedmo\Tree\Strategy\AbstractMaterializedPath;
use MongoDB\BSON\Regex;
use MongoDB\BSON\UTCDateTime;

/**
 * This strategy makes tree using materialized path strategy
 *
 * @author Gustavo Falco <comfortablynumb84@gmail.com>
 * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
class MaterializedPath extends AbstractMaterializedPath
{
    /**
     * {@inheritdoc}
     */
    public function removeNode($om, $meta, $config, $node)
    {
        $uow = $om->getUnitOfWork();
        $wrapped = AbstractWrapper::wrap($node, $om);

        // Remove node's children
        $results = $om->createQueryBuilder()
            ->find($meta->name)
            ->field($config['path'])->equals(new Regex('^'.preg_quote($wrapped->getPropertyValue($config['path'])).'.?+'))
            ->getQuery()
            ->execute();

        foreach ($results as $node) {
            $uow->scheduleForDelete($node);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getChildren($om, $meta, $config, $originalPath)
    {
        return $om->createQueryBuilder()
            ->find($meta->name)
            ->field($config['path'])->equals(new Regex('^'.preg_quote($originalPath).'.+'))
            ->sort($config['path'], 'asc')      // This may save some calls to updateNode
            ->getQuery()
            ->execute();
    }

    /**
     * {@inheritdoc}
     */
    protected function lockTrees(ObjectManager $om, AdapterInterface $ea)
    {
        $uow = $om->getUnitOfWork();

        foreach ($this->rootsOfTreesWhichNeedsLocking as $oid => $root) {
            $meta = $om->getClassMetadata(get_class($root));
            $config = $this->listener->getConfiguration($om, $meta->name);
            $lockTimeProp = $meta->getReflectionProperty($config['lock_time']);
            $lockTimeProp->setAccessible(true);
            $lockTimeValue = new UTCDateTime();
            $lockTimeProp->setValue($root, $lockTimeValue);
            $changes = [
                $config['lock_time'] => [null, $lockTimeValue],
            ];

            $ea->recomputeSingleObjectChangeSet($uow, $meta, $root);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function releaseTreeLocks(ObjectManager $om, AdapterInterface $ea)
    {
        $uow = $om->getUnitOfWork();

        foreach ($this->rootsOfTreesWhichNeedsLocking as $oid => $root) {
            $meta = $om->getClassMetadata(get_class($root));
            $config = $this->listener->getConfiguration($om, $meta->name);
            $lockTimeProp = $meta->getReflectionProperty($config['lock_time']);
            $lockTimeProp->setAccessible(true);
            $lockTimeValue = null;
            $lockTimeProp->setValue($root, $lockTimeValue);
            $changes = [
                $config['lock_time'] => [null, null],
            ];

            $ea->recomputeSingleObjectChangeSet($uow, $meta, $root);

            unset($this->rootsOfTreesWhichNeedsLocking[$oid]);
        }
    }
}
