<?php
namespace Newland\NeosCommon\Command;

/*
 * This file is part of the "neos-common" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use function Safe\json_encode;

/**
 * Abstract command controller.
 *
 * Base for all command controllers.
 *
 * @Flow\Scope("singleton")
 */
abstract class AbstractCommandController extends CommandController
{
    const EVENT_PROGRESS = 'progress';
    const EVENT_START = 'start';
    const EVENT_END = 'end';

    /**
     * @var \Closure[][]
     */
    protected $eventListeners = [];

    protected function showProgressOnCommandLine(): void
    {
        $label = function (string $type, array $additional) {
            $label = ' ' . $type;
            if (!empty($additional)) {
                $label .= ' ' . json_encode($additional);
            }
            return $label;
        };

        $this->on(static::EVENT_START, function (string $type, array $additional = []) use ($label) {
            $this->output->progressStart(0);
            $this->output->output($label($type, $additional) . "\r");
        });
        $this->on(
            static::EVENT_PROGRESS,
            function (string $type, int $processed, array $additional = []) use ($label) {
                $this->output->progressSet($processed);
                $this->output->output($label($type, $additional) . "\r");
            }
        );
        $this->on(static::EVENT_END, function (string $type, array $additional = []) use ($label) {
            $this->output->progressFinish();
            $this->output->outputLine($label($type, $additional));
        });
    }


    protected function on(string $event, \Closure $listener): void
    {
        if (!array_key_exists($event, $this->eventListeners)) {
            $this->eventListeners[$event] = [];
        }

        $this->eventListeners[$event][] = $listener;
    }


    /**
     * @Flow\Signal()
     * @param string $type
     * @param array $payload
     */
    protected function emitStart(string $type, array $payload = []): void
    {
        $this->callListeners('start', $type, $payload);
    }

    /**
     * @Flow\Signal()
     * @param string $type
     * @param array $payload
     */
    protected function emitEnd(string $type, array $payload = []): void
    {
        $this->callListeners('end', $type, $payload);
    }

    /**
     * @Flow\Signal()
     * @param string $type
     * @param int $processedElements
     * @param array $payload
     */
    protected function emitProgress(string $type, int $processedElements, array $payload = []): void
    {
        $this->callListeners('progress', $type, $processedElements, $payload);
    }

    /**
     * @param mixed ...$payload
     */
    protected function callListeners(string $event, ...$payload): void
    {
        if (!array_key_exists($event, $this->eventListeners)) {
            return;
        }

        foreach ($this->eventListeners[$event] as $listener) {
            $listener(...$payload);
        }
    }
}
