<?php

namespace IMATHUZH\Qfq\Core\Renderer\FormElement;

use IMATHUZH\Qfq\Core\Form\FormElement\AbstractFormElement;
use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
use IMATHUZH\Qfq\Core\Helper\Support;

abstract class NativeRenderer extends FormElementRenderer {

    /**
     * @var array
     * Defines the html tags that wrap label, input and note. This is initialized in $this->wrapLabelInputNote
     */
    public array $wrap = array();

    /**
     * @param AbstractFormElement $fe
     * @return string
     * @throws \CodeException
     * @throws \UserFormException
     */
    public function renderHtml(AbstractFormElement $fe): string {
        $hidden = ($fe->attributes[FE_MODE] == FE_MODE_HIDDEN) ? 'hidden' : '';

        // Setup Wrap elements for label, input and note
        $this->wrapLabelInputNote($fe->htmlAttributes[HTML_ATTR_ID], $fe->attributes[FE_BS_LABEL_COLUMNS], $fe->attributes[FE_BS_INPUT_COLUMNS], $fe->attributes[FE_BS_NOTE_COLUMNS], $fe->attributes[FE_MODE], $fe->attributes);
        $rowId = $fe->htmlAttributes[HTML_ATTR_ID] . "-r";
        $rowStart = "<div id='$rowId' class='form-group clearfix $hidden'>";
        $rowEnd = "</div>";

        // Custom Row Wrap
        if (isset($fe->attributes[FE_WRAP_ROW])) {
            list($rowStart, $rowEnd) = $this->customWrap($fe->attributes[FE_WRAP_ROW], $rowId, "form-group clearfix $hidden", '');
        }

        $inputElementHTML = $this->renderInput($fe);
        if (isset($fe->attributes[FE_INPUT_TOGGLE])) {
            $inputElementHTML = HelperFormElement::buildToggleInput($inputElementHTML, $fe);
        }

        return $fe->attributes[FE_HTML_BEFORE]
            . ($fe->attributes[FE_FLAG_ROW_OPEN_TAG] == 1 ? $rowStart : '')
            . $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START]
            . $this->renderLabel($fe)
            . $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END]
            . $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START]
            . $inputElementHTML
            . $this->renderToolTip($fe)
            . $this->renderFormEditorUrl($fe)
            . $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END]
            . $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START]
            . $this->renderNote($fe)
            . $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END]
            . ($fe->attributes[FE_FLAG_ROW_CLOSE_TAG] == 1 ? $rowEnd : '')
            . $fe->attributes[FE_HTML_AFTER];
    }

    /**
     * @param AbstractFormElement $fe
     * @return string
     * @throws \CodeException
     */
    protected function renderLabel(AbstractFormElement $fe): string {
        if ($fe->attributes[FE_BS_LABEL_COLUMNS] == 0) return "";
        $additionalCssClasses = is_array($fe->addClassRequired) ? implode(' ', $fe->addClassRequired) : $fe->addClassRequired;
        return HelperFormElement::buildLabel($fe->htmlAttributes[HTML_ATTR_NAME], $fe->attributes[FE_LABEL], $additionalCssClasses);
    }

    /**
     * Renders only the Input element. No label, note or additional wrappers around the input is rendered.
     * This function is public, so that it can be used to create inline-edit elements.
     *
     * @param AbstractFormElement $fe
     * @return string
     */
    public abstract function renderInput(AbstractFormElement $fe): string;

    /**
     * Renders the note part of the formElement.
     *
     * @param AbstractFormElement $fe
     * @return string
     */
    protected function renderNote(AbstractFormElement $fe): string {
        if ($fe->attributes[FE_BS_NOTE_COLUMNS] == 0) return "";
        if (!empty($fe->addClassRequired[FE_NOTE])) {
            $fe->attributes[FE_NOTE] = Support::wrapTag('<span class="' . $fe->addClassRequired[FE_NOTE] . '">', $fe->attributes[FE_NOTE]);
        }
        return $fe->attributes[FE_NOTE];
    }

    /**
     * Fill the BS wrapper for Label/Input/Note.
     * For legacy reasons, $label/$input/$note might be a number (0-12) or the bs column classes ('col-md-12 col-lg-9')
     * If a Renderer is implemented which does not use the BS column system, then this functionality will have to be moved elsewhere.
     *
     * @param $htmlId
     * @param $label
     * @param $input
     * @param $note
     * @return string
     */
    protected function wrapLabelInputNote($htmlId, $label, $input, $note, $mode, $formElement): string {
        $output = "";
        $attributeReadonly = $mode == FE_MODE_READONLY ? 'readonly="readonly"' : '';

        // Label
        $label = is_numeric($label) ? ($label == 0 ? 'hidden' : 'col-md-' . $label) : $label;
        $labelStart = "<div id='$htmlId-l' class='$label qfq-label'>";
        $labelEnd = "</div>";

        // Custom Label
        if (isset($formElement[FE_WRAP_LABEL])) {
            list($labelStart, $labelEnd) = $this->customWrap($formElement[FE_WRAP_LABEL], $htmlId, $label, '-l');
        }

        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = $labelStart;
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END] = $labelEnd;
        // apply Label Align style
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = Support::insertAttribute($this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START], HTML_ATTR_STYLE, 'text-align: ' . $formElement[F_FE_LABEL_ALIGN] . ';');


        // Input
        $input = is_numeric($input) ? ($input == 0 ? 'hidden' : 'col-md-' . $input) : $input;
        $inputStart = "<div id='$htmlId-i' class='$input' $attributeReadonly>";
        $inputEnd = "</div>";

        // Custom Input
        if (isset($formElement[FE_WRAP_INPUT])) {
            list($inputStart, $inputEnd) = $this->customWrap($formElement[FE_WRAP_INPUT], $htmlId, $input, '-i');
            if ($attributeReadonly != '') {
                $inputStart = Support::insertAttribute($inputStart, 'readonly', "readonly");
            }
        }

        $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START] = $inputStart;
        $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END] = $inputEnd;


        // Note
        $note = is_numeric($note) ? ($note == 0 ? 'hidden' : 'col-md-' . $note) : $note;
        $noteStart = "<div id='$htmlId-n' class='$note qfq-note'>";
        $noteEnd = "</div>";

        // Custom Note
        if (isset($formElement[FE_WRAP_NOTE])) {
            list($noteStart, $noteEnd) = $this->customWrap($formElement[FE_WRAP_NOTE], $htmlId, $note, '-n');
        }


        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = $noteStart;
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = $noteEnd;

        return $output;
    }

    /**
     *  Builds custom HTML wrapper start and end tags based on a pipe-separated string. E.g "<div ...>|</div>"
     *
     * @param string $wrap The custom wrapper string, e.g., "<div ...>|</div>".
     * @param string $htmlId The base ID to inject into the opening tag.
     * @param string $class CSS class string to inject into the opening tag.
     * @param string $idSuffix Suffix to append to $htmlId to generate a unique ID for this wrapper type (e.g. '-l', '-i', '-n').
     * @return array            An array with two elements: [wrapped opening tag, closing tag].
     * @throws \CodeException
     * @throws \UserFormException
     */
    protected function customWrap(string $wrap, string $htmlId, string $class, string $idSuffix): array {

        $wrapArray = explode('|', $wrap, 2);

        if (count($wrapArray) != 2) {
            throw new \UserFormException(
                "Need open & close wrap token for FormElement.parameter - E.g.: <div ...>|</div>",
                ERROR_MISSING_VALUE
            );
        }

        $start = $wrapArray[0];
        $end = $wrapArray[1];

        if ($start !== '') {
            $start = Support::insertAttribute($start, HTML_ATTR_ID, $htmlId . "$idSuffix");
            $start = Support::insertAttribute($start, HTML_ATTR_CLASS, $class);
        }

        return [$start, $end];

    }

    /**
     * @param $fe
     * @return string
     * @throws \CodeException
     * @throws \UserFormException
     */
    public function renderToolTip($fe): string {
        // Don't render tooltip icon for inline elements when not in debug mode
        // (the tooltip is still available via title attribute on the input element)
        if (($fe->attributes[FE_INLINE_FILTER] ?? false) && !$fe->showDebugInfoFlag) {
            return '';
        }

        $tooltip = '';
        if (strlen($fe->attributes[FE_TOOLTIP]) > 0) {
            $tooltip = Support::doTooltip($fe->htmlAttributes['id'] . '-t', $fe->attributes[FE_TOOLTIP]);
        }
        return $tooltip;
    }

}