<?php

namespace Medienreaktor\Energize\Service;


use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
use TYPO3\CMS\Core\Utility\DebugUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\SingletonInterface;

/**
 * Class FieldConfService
 *
 * This service is part of an API which replaces some functionality of TCA / TCEForms
 * It delivers items for TCA select fields which can be configured
 * in a .yaml file (EXT:energize/Configuration/yaml/EnergizeFieldConf.yaml)
 *
 * Goal of this API is to create a much simpler way to customize the items of a select field depending for example
 * on the chosen CType. Part of the improvement is the one file configuration, so no need to look into
 * multiple files with different file types.
 *
 * @package Medienreaktor\Energize\Service
 */
class FieldConfService implements SingletonInterface
{

    /**
     * @var null|YamlFileLoader
     */
    private $yamlFileLoader = null;

    /**
     * @var array
     */
    private $completeConf = [];

    /**
     * FieldConfService constructor which initializes some basic objects and reads the energize field configuration yaml file
     */
    public function __construct()
    {
        $this->yamlFileLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
        $this->completeConf = $this->yamlFileLoader->load("EXT:energize/Configuration/Yaml/EnergizeFieldConf.yaml");
    }

    /**
     * Returns the given fields' items ready to use in TCA
     *
     * @param $table string table name
     * @param $field string field name
     * @return array array with tca styled items, the array is empty if there was an error while loading the given fields configuration
     */
    public function getTcaItemsByField($table, $field)
    {
        $fieldConf = $this->getConfByField($table, $field);

        if (!$fieldConf || !is_array($fieldConf))
            return [];


        $this->applyDefaultOnFieldConf($fieldConf);
        return $this->convertFieldConfItemsToTcaItems($fieldConf['items']);
    }

    /**
     * Returns the given fields' configuration
     *
     * @param $table string table name
     * @param $field string field name
     * @return array|null returns the field configuration or null on failure
     */
    public function getConfByField($table, $field)
    {

        if($field == "layout"){
            return null;
        }

        $fieldConf = $this->completeConf[$table]['field'][$field];
        //process field conf if valid array returned
        if ($fieldConf && array_key_exists("items", $fieldConf)) {

            return $fieldConf;
        }

        return null;
    }

    /**
     * Applies the default option of a field conf by moving the item with that key/value to the top
     * If there is no default element set by field conf a Default element with empty value will be used
     *
     * @param $fieldConf array containing the field conf to process
     * @return bool shows whether a default field was set or not
     */
    private function applyDefaultOnFieldConf(&$fieldConf)
    {

        $defaultItem = [
            "" => [
                "label" => "Standard" // @ToDo: Apply proper language handling to default element
            ],
        ];

        if ((array_key_exists("default", $fieldConf) && $fieldConf["default"] !== "")
            && array_key_exists($fieldConf["default"], $fieldConf["items"])) {

            $defaultItem = [$fieldConf["default"] => $fieldConf["items"][$fieldConf["default"]]];
            unset($fieldConf["items"][$fieldConf["default"]]);
            unset($fieldConf["items"][""]);
        } else {
            $fieldConf["default"] = "";
        }

        $fieldConf["items"] = $defaultItem + $fieldConf["items"];

        return true;

    }

    /**
     * Converts an array of field conf items to a tca styled array of items
     *
     * @param $fieldConfItems array list of field conf items to process
     * @return array list of tca styled items
     */
    private function convertFieldConfItemsToTcaItems($fieldConfItems)
    {

        $tcaItems = [];
        foreach ($fieldConfItems as $key => $itemConf) {
            $tcaItems[] = $this->convertFieldConfItemToTcaItem($itemConf, $key);
        }
        return $tcaItems;
    }

    /**
     * Converts a field conf item to a tca styled item
     * target: https://docs.typo3.org/typo3cms/TCAReference/ColumnsConfig/Type/Select.html#items
     *
     * @param $itemConf array field conf item to process
     * @param $value string value of the field conf item / its key
     * @return array tca styled item
     */
    private function convertFieldConfItemToTcaItem($itemConf, $value)
    {
        if (strpos($value, '--div') !== false) {
            $value = '--div--';
        }
        return [
            (array_key_exists("label", $itemConf) ? $itemConf["label"] : ""),
            $value,
            (array_key_exists("icon", $itemConf) ? $itemConf["icon"] : ""),
            (array_key_exists("description", $itemConf) ? $itemConf["description"] : "")
        ];
    }

    /**
     * Returns the given fields' items merged with type based overrides ready to use in TCA
     *
     * @param $table string table name
     * @param $field string field name
     * @param $type string type name
     * @return array array with tca styled items, the array is empty if there was an error while loading the given fields configuration
     */
    public function getTcaItemsByType($table, $field, $type)
    {
        $fieldConf = $this->getConfByType($table, $field, $type);

        if (!$fieldConf || !is_array($fieldConf))
            return [];

        $this->applyDefaultOnFieldConf($fieldConf);
        return $this->convertFieldConfItemsToTcaItems($fieldConf['items']);
    }

    /**
     * Returns the given fields' configuration merged with type based overrides
     *
     * @param $table string table name
     * @param $field string field name
     * @param $type string type name
     * @return array|null returns the field configuration or null on failure
     */
    public function getConfByType($table, $field, $type)
    {
        if(!isset($this->completeConf[$table]['types'][$type][$field])){
            return null;
        }
        $fieldConf = $this->getConfByField($table, $field);
        $typeConf = $this->completeConf[$table]['types'][$type][$field];

        // Return field conf if there is no type conf
        if (!$typeConf)
            return $fieldConf;

        return $this->applyTypeConfOnFieldConf($fieldConf, $typeConf);
    }

    /**
     * Applies overrides and changes mentioned in the given type conf on the given field conf
     *
     * @param $fieldConf array original field conf
     * @param $typeConf array overrides and changes to apply
     * @return array processed field conf
     */
    private function applyTypeConfOnFieldConf($fieldConf, $typeConf)
    {
        $newFieldConf = $fieldConf;

        // if items are given via type conf override, don't care for anything else
        if (array_key_exists("items", $typeConf)) {
            $newFieldConf["items"] = $typeConf["items"];
        } // if not process remaining type conf stuff
        else {

            if (array_key_exists("removeItems", $typeConf)) {
                foreach (explode(",", $typeConf['removeItems']) as $keyToRemove) {
                    unset($newFieldConf['items'][trim($keyToRemove)]);
                }
            }

            if (array_key_exists("addItems", $typeConf) && is_array($typeConf["addItems"])) {
                foreach ($typeConf["addItems"] as $key => $itemConf) {
                    $newFieldConf["items"][$key] = $itemConf;
                }
            }

        }

        if (array_key_exists("default", $typeConf)) {
            $newFieldConf["default"] = $typeConf["default"];
        }

        return $newFieldConf;
    }

}
