<?php
/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Enterprise License (PEL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GPLv3 and PEL
 */

/**
 * ----------------------------------------------------------------------------------
 * based on @author ZF1 Zend_View_Helper_Placeholder_Registry
 * ----------------------------------------------------------------------------------
 */

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */

namespace Pimcore\Templating\Helper\Placeholder;

use Pimcore\Event\DocumentEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Registry for placeholder containers
 */
class ContainerService
{
    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @var int
     */
    private $currentIndex = 0;

    /**
     * Placeholder containers
     *
     * @var array
     */
    protected $_items = [];

    public function __construct()
    {
        $this->_items[$this->currentIndex] = [];
    }

    /**
     * TODO Pimcore 6 set event dispatcher as constructor parameter
     *
     * @internal
     * @required
     *
     * @param EventDispatcherInterface $eventDispatcher
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;

        // lazily add event listeners - these listeners are only added if the container service is actually built
        // when rendering a new document, the index is pushed to create a new, empty context
        $eventDispatcher->addListener(DocumentEvents::RENDERER_PRE_RENDER, [$this, 'pushIndex']);
        $eventDispatcher->addListener(DocumentEvents::RENDERER_POST_RENDER, [$this, 'popIndex']);
    }

    public function pushIndex()
    {
        ++$this->currentIndex;

        if (isset($this->_items[$this->currentIndex])) {
            throw new \RuntimeException(sprintf('Items at index %d already exist', $this->currentIndex));
        }

        $this->_items[$this->currentIndex] = [];
    }

    public function popIndex()
    {
        if (0 === $this->currentIndex) {
            throw new \OutOfBoundsException('Current index is already at 0');
        }

        if (isset($this->_items[$this->currentIndex])) {
            unset($this->_items[$this->currentIndex]);
        }

        --$this->currentIndex;
    }

    /**
     * createContainer
     *
     * @param  string $key
     * @param  array $value
     *
     * @return Container
     */
    public function createContainer($key, array $value = [])
    {
        $key = (string) $key;

        $this->_items[$this->currentIndex][$key] = new Container($value);

        return $this->_items[$this->currentIndex][$key];
    }

    /**
     * Retrieve a placeholder container
     *
     * @param  string $key
     *
     * @return Container
     */
    public function getContainer($key)
    {
        $key = (string) $key;
        if (isset($this->_items[$this->currentIndex][$key])) {
            return $this->_items[$this->currentIndex][$key];
        }

        $container = $this->createContainer($key);

        return $container;
    }

    /**
     * Does a particular container exist?
     *
     * @param  string $key
     *
     * @return bool
     */
    public function containerExists($key)
    {
        $key = (string) $key;
        $return = array_key_exists($key, $this->_items[$this->currentIndex]);

        return $return;
    }

    /**
     * Set the container for an item in the registry
     *
     * @param  string $key
     * @param  Container $container
     *
     * @return ContainerService
     */
    public function setContainer($key, Container $container)
    {
        $key = (string) $key;
        $this->_items[$this->currentIndex][$key] = $container;

        return $this;
    }

    /**
     * Delete a container
     *
     * @param  string $key
     *
     * @return bool
     */
    public function deleteContainer($key)
    {
        $key = (string) $key;
        if (isset($this->_items[$this->currentIndex][$key])) {
            unset($this->_items[$this->currentIndex][$key]);

            return true;
        }

        return false;
    }
}
