<?php

namespace Internetgalerie\IgDatapoolFe\ViewHelpers\Form;

use Closure;
use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper;
use TYPO3\CMS\Extbase\Error\Result;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;

/**
 * Display a field error
 *
 *         <dp:form.fieldError property="title" as="errorResult">Error<f:debug>{errorResult}</f:debug></dp:form.fieldError>
 *         <dp:form.fieldError property="title"><f:then>Error</f:then><f:else>OK</f:else></dp:form.fieldError>
 */
class FieldErrorViewHelper extends AbstractConditionViewHelper
{
    /**
     * Initialize the arguments.
     *
     * @return void
     * @api
     */
    public function initializeArguments(): void
    {
        $this->registerArgument('property', 'string', 'Formfield.', true, '');
        $this->registerArgument('as', 'string', 'name for errorResult object', false, 'errorResult');
    }

    /**
     * @param  array                     $arguments
     * @param  RenderingContextInterface $renderingContext
     * @return bool
     */
    public static function verdict(array $arguments, RenderingContextInterface $renderingContext)
    {
        $mappingResultsForProperty = static::getMappingResultsForProperty($arguments, $renderingContext);
        return $mappingResultsForProperty->hasErrors();
    }


    /**
     * Renders <f:then> child if $condition is true, otherwise renders <f:else> child.
     * Method which only gets called if the template is not compiled. For static calling,
     * the then/else nodes are converted to closures and condition evaluation closures.
     *
     * @return string the rendered string
     * @api
     */
    public function render(): string
    {
        $templateVariableContainer = $this->renderingContext->getVariableProvider();
        $templateVariableContainer->add($this->arguments['as'], static::getMappingResultsForProperty($this->arguments, $this->renderingContext));
        if (static::verdict($this->arguments, $this->renderingContext)) {
            $output = $this->renderThenChild();
        } else {
            $output = $this->renderElseChild();
        }
        $templateVariableContainer->remove($this->arguments['as']);
        return $output;
    }

    /**
     * Static rendering
     *
     * @param  array                     $arguments
     * @param Closure $renderChildrenClosure
     * @param  RenderingContextInterface $renderingContext
     * @return mixed
     */
    public static function renderStatic(array $arguments, Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
    {
        $templateVariableContainer = $renderingContext->getVariableProvider();
        $templateVariableContainer->add($arguments['as'], static::getMappingResultsForProperty($arguments, $renderingContext));
        $output = '';
        if (static::verdict($arguments, $renderingContext)) {
            if (isset($arguments['then'])) {
                $output = $arguments['then'];
            }
            if (isset($arguments['__thenClosure'])) {
                $output = $arguments['__thenClosure']();
            }
        } elseif (!empty($arguments['__elseClosures'])) {
            $elseIfClosures = $arguments['__elseifClosures'] ?? [];
            $output = static::evaluateElseClosures($arguments['__elseClosures'], $elseIfClosures, $renderingContext);
        } elseif (array_key_exists('else', $arguments)) {
            $output = $arguments['else'];
        }
        $templateVariableContainer->remove($arguments['as']);

        return $output;
    }
    /**
     * @param  array                     $closures
     * @param  array                     $conditionClosures
     * @param  RenderingContextInterface $renderingContext
     * @return string
     */
    private static function evaluateElseClosures(array $closures, array $conditionClosures, RenderingContextInterface $renderingContext)
    {
        foreach ($closures as $elseNodeIndex => $elseNodeClosure) {
            if (!isset($conditionClosures[$elseNodeIndex])) {
                return $elseNodeClosure();
            } else {
                if ($conditionClosures[$elseNodeIndex]()) {
                    return $elseNodeClosure();
                }
            }
        }
        return '';
    }
    /**
     * Internal method which checks if we should evaluate a domain object or just output arguments['name'] and arguments['value']
     *
     * @param  array                     $arguments
     * @param  RenderingContextInterface $renderingContext
     * @return bool TRUE if we should evaluate the domain object, FALSE otherwise.
     */
    protected static function isObjectAccessorMode(array $arguments, RenderingContextInterface $renderingContext)
    {
        return $arguments['property'] && $renderingContext->getViewHelperVariableContainer()->exists(
            FormViewHelper::class,
            'formObjectName'
        );
    }

    /**
     * Shortcut for retrieving the request from the controller context
     *
     * @return Request
     */
    protected function getRequest()
    {
        return $this->renderingContext->getRequest();
    }

    /**
     * Get errors for the property and form name of this ViewHelper
     *
     * @param array                     $arguments
     * @param RenderingContextInterface $renderingContext
     *
     * @return Result Array of errors
     */
    protected static function getMappingResultsForProperty(array $arguments, RenderingContextInterface $renderingContext)
    {
        if (!static::isObjectAccessorMode($arguments, $renderingContext)) {
            return new Result();
        }
        $originalRequestMappingResults = $renderingContext->getRequest()->getAttribute('extbase')->getOriginalRequestMappingResults();
        $formObjectName = $renderingContext->getViewHelperVariableContainer()->get(
            FormViewHelper::class,
            'formObjectName'
        );
        return $originalRequestMappingResults->forProperty($formObjectName)->forProperty($arguments['property']);
    }
}
