<?php

namespace Internetgalerie\IgDatapoolFe\ViewHelpers\Form;

use Exception;
use Traversable;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use Internetgalerie\IgDatapoolFe\Domain\Interfaces\RecursiveObjectInterface;

/**
 * Provides methods for autloading
 */
trait AutoloadTrait
{
    protected $recursiveness = [];
    protected $extraValues = [];

    protected function autoload()
    {
        if (is_string($this -> arguments['options'])) {
            //parse the path
            $path = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('->', $this -> arguments['options']);
            $args = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('(', $path[1]);
            //path[0] is the object
            $path[1] = $args[0];
            $path[2] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', trim($args[1], '()'));
            $path[2] = array_map(
                fn($s) => trim((string) $s, "'"),
                $path[2]
            );

            //try to get the repository and get the objects from it
            $req = $this->renderingContext->getRequest();

            $newOptions = [];
            $repository = GeneralUtility::makeInstance($req -> getControllerVendorName() . '\\' . $req -> getControllerExtensionName() . '\\Domain\\Repository\\' . $path[0]);

            $newOptions = call_user_func_array([$repository, $path[1]], $path[2]);

            if (!is_a($newOptions, '\TYPO3\CMS\Extbase\Persistence\Generic\QueryResult')) {
                throw new Exception('Errror: did you perhaps specify a non-existing repository method?');
            }
            $this -> arguments['options'] = $newOptions;
        }
        $this -> convertObjToArray();
    }

    /**
     * Render the option tags.
     *
     * @return array an associative array of options, key will be the value of the option tag
     */
    protected function convertObjToArray()
    {
        $optionsArgument = $this -> arguments['options'];
        if (!is_array($optionsArgument) && !$optionsArgument instanceof Traversable) {
            return [];
        }
        $this -> arguments['options'] = $this -> convertObjToArrayRec($optionsArgument, 0);
    }

    protected function convertObjToArrayRec($optionsArgument, $level = 0)
    {
        $options = [];
        foreach ($optionsArgument as $key => $value) {
            $object = $value;
            if (is_object($value)) {
                if ($this -> hasArgument('optionValueField')) {
                    $key = ObjectAccess::getPropertyPath($value, $this -> arguments['optionValueField']);
                    if (is_object($key)) {
                        if (method_exists($key, '__toString')) {
                            $key = (string)$key;
                        } else {
                            throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('Identifying value for object of class "' . $value::class . '" was an object.', 1247827428);
                        }
                    }
                } elseif ($this -> persistenceManager -> getIdentifierByObject($value) !== null) {
                    $key = $this -> persistenceManager -> getIdentifierByObject($value);
                } elseif (method_exists($value, '__toString')) {
                    $key = (string)$value;
                } else {
                    throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('No identifying value for object of class "' . $value::class . '" found.', 1247826696);
                }
                if ($this -> hasArgument('optionLabelField')) {
                    if ($this -> hasArgument('optionClass')) {
                        //$valueExtraValues = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($value, $this -> hasArgument('optionClass'));
                        $this->extraValues[$key]= $value; //save objects for later use
                    }



                    $value = ObjectAccess::getPropertyPath($value, $this -> arguments['optionLabelField']);
                    if (is_object($value)) {
                        if (method_exists($value, '__toString')) {
                            $value = (string)$value;
                        } else {
                            throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception('Label value for object of class "' . $value::class . '" was an object without a __toString() method.', 1247827553);
                        }
                    }
                } elseif (method_exists($value, '__toString')) {
                    $value = (string)$value;
                } elseif ($this -> persistenceManager -> getIdentifierByObject($value) !== null) {
                    $value = $this -> persistenceManager -> getIdentifierByObject($value);
                }
            }

            //set key to __, if it is not selectable
            if ($object instanceof RecursiveObjectInterface) {
                $key = $object -> isSelectable() ? $key : '__' . $key;
            }

            // add it to the array
            $options[$key] = $value;





            $this -> recursiveness[$key] = $level;
            if ($object instanceof RecursiveObjectInterface) {
                $childOpts = $this -> convertObjToArrayRec($object -> getChildren(), $level + 1);
                foreach ($childOpts as $key => $op) {
                    $options[$key] = $op;
                    $this -> recursiveness[$key.md5((string) $value)] = $level + 1;
                }
            }
        }
        return $options;
    }

    /**
     * Get the option value for an object
     *
     * @param  mixed $valueElement
     * @return string
     */
    protected function convertObjectToScalar($valueElement)
    {
        if (is_object($valueElement)) {
            if ($this -> hasArgument('optionValueField')) {
                return ObjectAccess::getPropertyPath($valueElement, $this -> arguments['optionValueField']);
            } else {
                if ($this -> persistenceManager -> getIdentifierByObject($valueElement) !== null) {
                    return $this -> persistenceManager -> getIdentifierByObject($valueElement);
                } else {
                    return (string)$valueElement;
                }
            }
        } else {
            return $valueElement;
        }
    }
}
