<?php

namespace Internetgalerie\IgDatapoolFe\ViewHelpers\Form;

use Internetgalerie\IgDatapoolFe\ViewHelpers\FieldWrapperViewHelper;
use TYPO3\CMS\Extbase\Error\Result;
use TYPO3\CMS\Extbase\Error\Error;
use Internetgalerie\IgDatapoolFe\ViewHelpers\FormViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use Internetgalerie\IgDatapoolFe\ViewHelpers\SearchFormViewHelper;
use Internetgalerie\IgDatapoolFe\Utility\NeedfulThings;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
/**
 * Provides accessors for use with the tabs / fieldwarpper engine.
 */
trait FieldTrait
{
    /**
     * the fields id
     *
     * @var $fieldId string
     */
    protected $fieldId = '';

    /**
     * Debug
     */
    public static $forceError = false;

    /**
     * Inject the ID Argument. has to be done this way because of the caching stuff...
     */
    public function setArguments(array $arguments): void
    {

        //doesnt really fit here, but it works
        FieldWrapperViewHelper::setField($this);

        if (null === $arguments['id']) {
            if ($this->isSearchForm()) {
                $this->fieldId = $this->generateSearchId($arguments);
            } else {
                $this->fieldId = $this->generateId($arguments);
            }
        }
        if ($this->fieldId) {
            $arguments['id'] = $this->fieldId;
        }

        /*
         * Override defaults
         * we REALLY want our own error class
         */
        if (!isset($arguments['errorClass']) || $arguments['errorClass'] == 'f3-form-error') {
            $arguments['errorClass'] = 'dp-field-error';
        }
        parent::setArguments($arguments);
    }

    /**
     * Check if field has a validation error
     *
     * @return boolean
     */
    public function hasError()
    {
        if (self::$forceError) {
            return true;
        }
        return $this->getMappingResultsForProperty()->hasErrors();
    }

    protected function setErrorClassAttribute(): void
    {
        parent::setErrorClassAttribute();
        if (self::$forceError) {
            $class = $this->tag->getAttribute('class');
            $this->tag->addAttribute('class', $class . ' ' . $this->arguments['errorClass']);
        }
    }

    /**
     * get all validation errors
     *
     * @return Result
     */
    public function getErrors()
    {
        if (self::$forceError) {
            $k = new Result();
            $k->addError(new Error('Demo Fehlermeldung'));
            return $k;
        }
        return $this->getMappingResultsForProperty();
    }

    /**
     * Return the whole path according to the model.field scheme
     *
     * @param  string $separator How to separate both parts
     * @return string
     */
    public function getNamePath($separator = '.')
    {
        if ($this->isSearchForm()) {
            return 'search' . $separator . $this->arguments['name'];
        }
        $out = '';
        if (null != FormViewHelper::$curForm) {
            $out = strtolower(FormViewHelper::$curForm->_getFormName()) . $separator;
        }
        return $out . $this->arguments['property'];
    }

    /**
     * Returns the property that the formfield is bound to
     */
    public function getProperty()
    {
        return $this->arguments['property'];
    }

    /**
     * get the fields Id as written in the HTML
     *
     * @return string
     */
    public function getId()
    {
        return $this->fieldId;
    }

    /**
     * Generates a HTML Id
     */
    protected function generateId($arguments)
    {
        if (null == FormViewHelper::$curForm) {
            throw new Exception('This field MUST be used inside DataPool Form!');
        }
        //form name.
        $fieldId = 'dp-field-' . str_replace('.', '-', strtolower(FormViewHelper::$curForm->_getFormName()) . '-' . $arguments['property']);
        if ($this->isMultipleValuedField()) {
            $fieldId .= '-' . $arguments['value'];
        }
        return $fieldId;
    }

    /**
     * Generates a HTML Id
     */
    protected function generateSearchId($arguments)
    {
        if (null == FormViewHelper::$curForm) {
            throw new Exception('This field MUST be used inside DataPool Form!');
        }
        //form name.
        $fieldId = 'dp-search-' . $arguments['name'];
        if ($this->isMultipleValuedField()) {
            $fieldId .= '-' . $arguments['value'];
        }
        return $fieldId;
    }

    /**
     * is this a multivalued field like checkboxes or radio boxes?
     */
    protected function isMultipleValuedField()
    {
        $all = [RadioViewHelper::class, CheckboxViewHelper::class];
        return in_array($this::class, $all);
    }

    protected function isSearchForm()
    {
        return FormViewHelper::$curForm  !==null  && FormViewHelper::$curForm->isSearchForm();
    }

    /**
     * Add support for default value. must be registred by the specific fieldtype!!
     *
     * @see TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
     *
     * Get the value of this form element.
     * Either returns arguments['value'], or the correct value for Object Access.
     *
     * @param  boolean $convertObjects whether or not to convert objects to identifiers
     * @return mixed Value
     */

    protected function getValueAttribute($convertObjects = true)
    {
        // EDIT MB
        // Searchform Value
        //if ($this->isSearchForm()) {
        if ($this->viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formClass')) {
            $formClass = $this->viewHelperVariableContainer->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formClass');

            if ($formClass==SearchFormViewHelper::class) {
                $requestArguments = [];
                if ($this->getRequest()->hasArgument('@search')) {
                    $requestArguments = $this->getRequest()->getArgument('@search');
                    if (isset($requestArguments[$this->arguments['name']])) {
                        return $requestArguments[$this->arguments['name']];
                    }
                }
            }
        }
        // end edit mb

        $value = null;
        if ($this->hasArgument('value')) {
            $value = $this->arguments['value'];
        } elseif ($this->hasMappingErrorOccurred()) {
            $value = $this->getLastSubmittedFormData();

        // here we have to add additionalidedentiyfield!!!!
        } elseif ($this->isObjectAccessorMode()) {
            $value = $this->getPropertyValue();

            // XXX: Is this line correct here? only works with ObjectAccessorMode

            if (null === $value && isset($this->arguments['default'])) {
                $value = $this->arguments['default'];
            }
        }
        if ($convertObjects === true && is_object($value)) {
            $identifier = $this->persistenceManager->getIdentifierByObject($value);
            if ($identifier !== null) {
                $value = $identifier;
            }
        }
        if ($this->viewHelperVariableContainer->exists(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObject')) {
            $this->addAdditionalIdentityPropertiesIfNeeded();
        }
        return $value;
    }













    /**
     * Checks if a property mapping error has occurred in the last request.
     *
     * @return bool TRUE if a mapping error occurred, FALSE otherwise
     */
    protected function hasMappingErrorOccurred(): bool
    {
        $ret = $this->getRequest()->getOriginalRequest() !== null;
        //DebuggerUtility::var_dump($this->getRequest());
        return $ret;
    }

    /**
     * adjusted for searchform
     * use _ to avoid autowrapping inside a searchform
     */
    protected function getNameWithoutPrefix(): string
    {
        if ($this->isSearchForm()) {
            if (str_starts_with((string) $this->arguments['name'], '_')) {
                return trim((string) $this->arguments['name'], '_');
            }
            return '@search[' . $this->arguments['name'] . ']';
        }

        return parent::getNameWithoutPrefix();
    }

    /**
     * Automagically add the reuired flag if property has an accroding validator
     */
    protected function isRequiredByAnnotation()
    {
        return NeedfulThings::isRequiredByAnnotation($this->getProperty());
    }

    /**
     * Renders a hidden form field containing the technical identity of the given object.
     *
     * @param mixed $object Object to create the identity field for. Non-objects are ignored.
     * @param  string $name   Name
     * @return string A hidden field containing the Identity (UID in TYPO3 Flow, uid in Extbase) of the given object or NULL if the object is unknown to the persistence framework
     * @see    \TYPO3\CMS\Extbase\Mvc\Controller\Argument::setValue()
     */
    protected function renderHiddenIdentityField(mixed $object, ?string $name): string
    {
        if ($object instanceof LazyLoadingProxy) {
            $object = $object->_loadRealInstance();
        }
        if (!is_object($object) || !($object instanceof AbstractDomainObject) || ($object->_isNew() && !$object->_isClone())) {
            return '';
        }
        // Intentionally NOT using PersistenceManager::getIdentifierByObject here!!
        // Using that one breaks re-submission of data in forms in case of an error.
        $identifier = $object->getUid();
        if ($identifier === null) {
            return chr(10) . '<!-- Object of type ' . $object::class . ' is without identity -->' . chr(10);
        }
        $newName = $this->prefixFieldName($name) . '[__identity]';
        $this->registerFieldNameForFormTokenGeneration($name);

        //EDIT MB: add a class
        return chr(10) . '<input type="hidden" name="' . $newName . '" value="' . $identifier . '" class="' . trim(str_replace(['][', '['], '-', $name), '[]') . '" />' . chr(10);
        //END EDIT MB.
    }
}
