<?php

namespace Internetgalerie\IgDatapoolFe\ViewHelpers\Form;

use Internetgalerie\IgDatapoolFe\ViewHelpers\SearchFormViewHelper;
use Traversable;

/**
 * View Helper which creates a simple radio button (<input type="radio">).
 *
 * = Examples =
 *
 * <code title="Example">
 * <f:form.radio name="myRadioButton" value="someValue" />
 * </code>
 * <output>
 * <input type="radio" name="myRadioButton" value="someValue" />
 * </output>
 *
 * <code title="Preselect">
 * <f:form.radio name="myRadioButton" value="someValue" checked="{object.value} == 5" />
 * </code>
 * <output>
 * <input type="radio" name="myRadioButton" value="someValue" checked="checked" />
 * (depending on $object)
 * </output>
 *
 * <code title="Bind to object property">
 * <f:form.radio property="newsletter" value="1" /> yes
 * <f:form.radio property="newsletter" value="0" /> no
 * </code>
 * <output>
 * <input type="radio" name="user[newsletter]" value="1" checked="checked" /> yes
 * <input type="radio" name="user[newsletter]" value="0" /> no
 * (depending on property "newsletter")
 * </output>
 *
 * @api
 * @author     Markus Baumgartner <mb@stylesheep.ch>, Internetgalerie AG
 * @package    IgDatapoolFE
 * @subpackage ViewHelpers
 */
class RadioViewHelper extends \Internetgalerie\IgCoreViewhelpers\ViewHelpers\Form\RadioViewHelper implements FieldInterface
{
    use FormFieldTrait;
    /*
      //use AutoloadTrait;
    //use FieldTrait {
    //    FieldTrait::getValueAttribute as traitGetValue;
    //    }
    */
    protected $multipleBoxes = false;

    protected $type = 'radio';

    public function initializeArguments(): void
    {
        parent::initializeArguments();
        $this->overrideArgument('value', 'string', 'Value of input tag.', false);
        $this->registerArgument('label', 'string', 'The Label', false, '');
        $this->registerArgument('renderArray', 'int', 'If render array or not', false, '1');
        $this->registerArgument('options', 'mixed', 'Associative array with internal IDs as key, and the values are displayed in the select box', false);
        $this->registerArgument('optionValueField', 'string', 'If specified, will call the appropriate getter on each object to determine the value.');
        $this->registerArgument('optionLabelField', 'string', 'If specified, will call the appropriate getter on each object to determine the label.');
        $this->registerArgument('cols', 'int', 'Columns', false, '1');
        $this->registerArgument('default', 'string', 'Default Value');
    }


    protected function generateId($arguments)
    {
        $fieldId= $fieldId = 'dp-field-' . str_replace('.', '-', strtolower((string) $arguments['property']));
        $fieldId.= '-' . $arguments['value'];
        return $fieldId;
    }

    /**
     * Renders the checkbox.
     *
     * @return string
     * @api
     */
    public function render(): string
    {
        $checked = $this->arguments['checked'];

        $this->multipleBoxes = $this->arguments['options'] != '';

        $this->tag->addAttribute('type', $this->type);
        $this->setErrorClassAttribute();

        $res = '';

        if (!$this->multipleBoxes) {
            $formClass=$this->getFormClass();
            if ($formClass==SearchFormViewHelper::class) {
                $this->arguments['id'] =  $this->generateSearchId($this->arguments);
            } else {
                $this->arguments['id'] =$this->generateId($this->arguments);
            }
            $res = $this->renderSingle($checked);
            return $res;
        }

        //call the autoloader, to resolve the options
        //$this->autoload();

        //just to be sure..
        $this->arguments['value'] = null;

        $k = 1;
        $opCount = count($this->arguments['options']);
        $perCol = ceil($opCount / max(1, intval($this->arguments['cols'])));

        $w = round(100 / max(1, intval($this->arguments['cols'])), 3);

        $errorClass = $this->hasError() ? ' dp-field-error' : '';

        //this could be decoupled
        $res .= '<div class="dp-checkboxes ym-grid' . $errorClass . '">
		<div class="dp-checkbox-col ig-inline ym-g' . floor($w) . '">';

        foreach ($this->arguments['options'] as $key => $label) {
            $this->arguments['value'] = $key;
            $this->arguments['label'] = $label;
            $this->arguments['id'] = $this->generateId($this->arguments);
            $res .= $this->renderSingle(null, $this->arguments['renderArray']);
            if ($k % $perCol == 0 && $k < $opCount) {
                $res .= '</div><div class="dp-checkbox-col ig-inline ym-g' . floor($w) . '" style="width:' . $w . '%">';
            }
            ++$k;
        }

        $res .= '</div></div>';

        return $res;
    }

    protected function renderSingle($checked = null, $multiValue=false)
    {
        $this->tag->removeAttribute('checked');
        $currentSubmitedValue = $this->getValueAttribute();
        if ($checked === null && $this->isObjectAccessorMode()) {

            //properrty value: what is currently selected
            if ($this->hasMappingErrorOccurred()) {
                $propertyValue = $this->getLastSubmittedFormData();
            } else {
                $propertyValue = $this->getPropertyValue();
            }
            // this MIGHT be an array/traversable, which means the poperty is a mn relations
            // lets create an array out of it
            if ($propertyValue instanceof Traversable) {
                $propertyValue = iterator_to_array($propertyValue);
            }


            // Ist eine Relation
            if (!is_array($propertyValue) && $multiValue) {
                $propertyValue=[];
            }

            if (is_array($propertyValue)) {
                if (count($propertyValue) == 0 && $this->hasArgument('default') && ((is_array($this->arguments['default']) && in_array($currentSubmitedValue, $this->arguments['default'])) || ($this->arguments['default'] == $currentSubmitedValue))) {
                    $checked = true;
                }
                //now this array could contain objects
                // so we check convert each object and check it against our value representation (which has to be astring of course)
                if ($checked == null) {
                    foreach ($propertyValue as $prop) {
                        // prop can still be an object!!
                        if ($propertyValue != null || !is_null($propertyValue)) {
                            $checked = $currentSubmitedValue == $this->convertObjectToScalar($prop);
                        }
                        if ($checked) {
                            break;
                        }
                    }
                }

                //
                // the code duplicaton here is on purpose!!!!
                //
                // now since we know we're dealing with a mn relation field or something similar,
                // we have to se tthe fieldname to for passing and array
                //
                $nameAttribute = $this->getName(true);
                $this->registerFieldNameForFormTokenGeneration($nameAttribute);
                $this->tag->addAttribute('name', $nameAttribute);
            } else {

                // no type-safe comparison by intention
                if ($propertyValue !== null || !is_null($propertyValue)) {
                    $checked = $propertyValue == $currentSubmitedValue;
                }

                //default
                if (is_null($propertyValue) && $propertyValue!==false && $this->hasArgument('default') && ((is_array($this->arguments['default']) && in_array($currentSubmitedValue, $this->arguments['default'])) || ($this->arguments['default'] == $currentSubmitedValue))) {
                    $checked = true;
                }

                // in this case it is a simpler field that doesn't expect an array.
                $nameAttribute = $this->getName(false);
                $this->registerFieldNameForFormTokenGeneration($nameAttribute);
                $this->tag->addAttribute('name', $nameAttribute);
            }
        } else {
            if (!$checked && $this->hasArgument('default') && ((is_array($this->arguments['default']) && in_array($currentSubmitedValue, $this->arguments['default'])) || ($this->arguments['default'] == $currentSubmitedValue))) {
                $checked = true;
            }

            // XXX: this is confusing
            // XXX: array values not supported correctly
            // valueAttribute  == submitted value
            // $this>arguments['value'] == definedvalue
            //set checked if: the request value (stored in valueAttribute) is equal to the defined value-argument.
            //          if ($this->hasMappingErrorOccurred()) {
            //  $currentSubmitedValue = $this->getLastSubmittedFormData();
            // }

            //          echo($this->arguments['checked'].'a'.$currentSubmitedValue .'()='.$currentSubmitedValue.'('.$this->arguments['value'].')' );
            if ($currentSubmitedValue==$this->arguments['value']) { //|| in_array($currentSubmitedValue,$this->arguments['value'])) //Kann dies ein Array sein? wenn ja->wieder rein
                $checked = true;
            }
            // reset the attribute. html attribute will ALWAYS be the given argument.
            //$currentSubmitedValue = $this->arguments['value'];

            $this->tag->addAttribute('name', $this->getName());
        }

        $value = $this->arguments['value'] ?? null;
        $this->tag->addAttribute('value', $value);
        if ($checked) {
            $this->tag->addAttribute('checked', 'checked');
        }
        $this->tag->addAttribute('id', $this->arguments['id']);
        /*
        if ($value && $this->recursiveness[$value] ?? false) {
            $this->tag->addAttribute('style', 'margin-left:' . intval($this->recursiveness[$value]) * 1.5 . 'em');
        }
        */
        return $this->tag->render() . $this->renderLabel();
    }

    protected function renderLabel()
    {
        if ($this->arguments['label']) {
            return '<label class="dp-box-label" for="' . $this->arguments['id'] . '">' . htmlspecialchars((string) $this->arguments['label']) . '&nbsp;</label>' . ($this->multipleBoxes ? '<br />' : '');
        }
    }

    protected function getName($addArray = false): string
    {
        return parent::getName() . ($addArray ? '[]' : '');
    }

}
