<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Templating\Widget;

use Chill\MainBundle\Templating\Events\DelegatedBlockRenderingEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
 * Add the function `chill_delegated_block`.
 *
 * In a template, you can now allow rendering of a block from other bundle.
 *
 *  The layout template must explicitly call the rendering of other block,
 *  with the twig function
 *
 *  ```
 *  chill_delegated_block('block_name', { 'array' : 'with context' } )
 *  ```
 *
 *  This will launch an event
 *  `Chill\MainBundle\Templating\Events\DelegatedBlockRenderingEvent` with
 *  the event's name 'chill_block.block_name'.
 *
 *  You may add content to the page using the function
 *  `DelegatedBlockRenderingEvent::addContent`.
 *
 * See also the documentation of
 * `Chill\MainBundle\Templating\Events\DelegatedBlockRenderingEvent`
 * for usage of this event class
 */
class WidgetRenderingTwig extends AbstractExtension
{
    /**
     * @var EventDispatcherInterface
     */
    protected $eventDispatcher;

    /**
     * Contains the widget. This is a double dimension array :.
     *
     * - first key is the place,
     * - second key is the ordering ;
     * - the value is an array where the widget is the first argument and the
     * second is the config
     *
     * i.e :
     *
     * $widget['place']['ordering'] = array($widget, $config);
     *
     * @var array an array of widget by place and ordering
     */
    protected $widget = [];

    public function __construct(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    /**
     * add a widget to this class, which become available for a future call.
     *
     * This function is used by DependencyInjection\CompilerPass\WidgetCompilerPass,
     * which add the widget to this class when it is created by from DI, according
     * to the given config under `chill_main`.
     *
     * @param string          $place
     * @param WidgetInterface $widget
     */
    public function addWidget($place, mixed $ordering, $widget, array $config = [])
    {
        $this->widget[$place][$ordering] = [$widget, $config];
    }

    public function getFunctions()
    {
        return [
            new TwigFunction(
                'chill_delegated_block',
                $this->renderingWidget(...),
                [
                    'is_safe' => ['html'],
                    'needs_environment' => true,
                    'deprecated' => true, 'alternative' => 'chill_widget',
                ]
            ),
            new TwigFunction(
                'chill_widget',
                $this->renderingWidget(...),
                ['is_safe' => ['html'], 'needs_environment' => true]
            ),
        ];
    }

    public function getName()
    {
        return 'chill_main_widget';
    }

    public function renderingWidget(Environment $env, $block, array $context = [])
    {
        // get the content of widgets
        $content = '';

        foreach ($this->getWidgetsArraysOrdered($block) as $a) {
            /** @var Widget\WidgetInterface $widget */
            $widget = $a[0];
            $config = $a[1];

            $content .= $widget->render($env, $block, $context, $config);
        }

        // for old rendering events (deprecated)
        $event = new DelegatedBlockRenderingEvent($context);

        $this->eventDispatcher->dispatch($event, 'chill_block.'.$block);

        return $content.' '.$event->getContent();
    }

    /**
     * @param string $place
     *
     * @return array
     */
    protected function getWidgetsArraysOrdered($place)
    {
        if (!\array_key_exists($place, $this->widget)) {
            $this->widget[$place] = [];
        }

        \ksort($this->widget[$place]);

        return $this->widget[$place];
    }
}
