<?php

namespace IMATHUZH\Qfq\Core\Form\FormElement;

use IMATHUZH\Qfq\Core\Form\Form;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Path;
use IMATHUZH\Qfq\Core\Helper\Support;

class ImageCutFormElement extends AbstractFormElement {
    public array $fabricHtmlAttributes = array();
    public array $inputHtmlAttributes = array();
    public array $inputBase64HtmlAttributes = array();

    public bool $isImageReadable = true;

    /**
     * @param $attributes
     * @param Form|null $form
     * @throws \CodeException
     * @throws \DbException
     * @throws \DownloadException
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     * @throws \UserFormException
     * @throws \UserReportException
     */
    public function __construct($attributes, ?Form $form = null) {
        parent::__construct($attributes, $form);

        // IF image file is given, check if it is readable.
        if (!empty($this->attributes[FE_IMAGE_SOURCE])) {

            if (!is_readable($this->attributes[FE_IMAGE_SOURCE])) {
                $this->isImageReadable = false;
                return;
            }

            // KEEP_ORIGINAL=0 >> do not try to load orig file.
            if (($this->attributes[FE_IMAGE_KEEP_ORIGINAL] ?? '1') == '1') {
                // If there is already an orig file: take that one as source
                $origFileName = HelperFile::getQfqOrigHiddenFilename($this->attributes[FE_IMAGE_SOURCE]);
                if (is_readable($origFileName)) {
                    $this->attributes[FE_IMAGE_SOURCE] = $origFileName;
                }
            }
        }

        $imageFileName = $this->fileToSipUrl($this->attributes[FE_IMAGE_SOURCE]);

        // Sanatize FE_IMAGE_OUTPUT_FORMAT
        switch ($this->attributes[FE_IMAGE_OUTPUT_FORMAT] ?? '') {
            case '':
                $this->attributes[FE_IMAGE_OUTPUT_FORMAT] = 'jpeg';
                break;
            case 'jpeg':
            case 'png':
                break;
            default:
                throw new \UserFormException("Unknown output format: " . $this->attributes[FE_IMAGE_OUTPUT_FORMAT], ERROR_UNKNOWN_MODE);
        }

        if (($this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG] ?? '') == '') {
            $this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG] = '0.8';
        }

        // Check for valid decimal
        if (!is_numeric($this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG])
            || $this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG] < 0
            || 1 < $this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG]) {
            throw new \UserFormException("JPG quality should be 0.0 <= ? <= 1.0" . $this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG], ERROR_INVALID_DECIMAL_FORMAT);
        }

        if (($this->attributes[FE_IMAGE_OUTPUT_WIDTH] ?? '') == '') {
            $this->attributes[FE_IMAGE_OUTPUT_WIDTH] = '150';
        }
        if (($this->attributes[FE_IMAGE_OUTPUT_HEIGHT] ?? '') == '') {
            $this->attributes[FE_IMAGE_OUTPUT_HEIGHT] = '200';
        }

        // Check for valid decimal
        if (!is_numeric($this->attributes[FE_IMAGE_OUTPUT_WIDTH]) || !is_numeric($this->attributes[FE_IMAGE_OUTPUT_HEIGHT])) {
            throw new \UserFormException("Output width and height should be numeric.", ERROR_INVALID_DECIMAL_FORMAT);
        }

        // Check for valid JSON "darken margin"
        if (($this->attributes[FE_IMAGE_DARKEN_MARGIN] ?? '') != '') {
            $result = json_decode($this->attributes[FE_IMAGE_DARKEN_MARGIN]);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \UserFormException(FE_IMAGE_DARKEN_MARGIN . ": invalid JSON" . $this->attributes[FE_IMAGE_DARKEN_MARGIN], ERROR_INVALID_VALUE);
            }
            // Encode the double ticks within the json
            $this->attributes[FE_IMAGE_DARKEN_MARGIN] = str_replace("\"", "&quot;", $this->attributes[FE_IMAGE_DARKEN_MARGIN]);
        }

        $htmlFabricTargetImageId = $this->attributes[FE_HTML_ID] . HTML_ID_TARGET_IMAGE; /* data-fabric-json-id */

        $this->fabricHtmlAttributes['class'] = IMAGE_ADJUST_CSS_CLASS;
        $this->fabricHtmlAttributes['data-image'] = $imageFileName;
        $this->fabricHtmlAttributes['data-base64-id'] = $htmlFabricTargetImageId;
        $this->fabricHtmlAttributes['data-fabric-json-id'] = $this->attributes[FE_HTML_ID];
        $this->fabricHtmlAttributes['data-output-format'] = $this->attributes[FE_IMAGE_OUTPUT_FORMAT];

        if (($this->attributes[FE_IMAGE_PREVIEW_HTML_ID] ?? '') != '') {
            $this->fabricHtmlAttributes['data-img-preview-id'] = $this->attributes[FE_IMAGE_PREVIEW_HTML_ID];
        }
        if ($this->attributes[FE_IMAGE_OUTPUT_FORMAT] == 'jpeg') {
            $this->fabricHtmlAttributes['data-output-format'] = $this->attributes[FE_IMAGE_OUTPUT_FORMAT];
            $this->fabricHtmlAttributes['data-output-quality-jpeg'] = $this->attributes[FE_IMAGE_OUTPUT_QUALITY_JPEG];
        }
        $this->fabricHtmlAttributes['data-output-width'] = $this->attributes[FE_IMAGE_OUTPUT_WIDTH];
        $this->fabricHtmlAttributes['data-output-height'] = $this->attributes[FE_IMAGE_OUTPUT_HEIGHT];
        $this->fabricHtmlAttributes['data-darken-margins'] = $this->attributes[FE_IMAGE_DARKEN_MARGIN] ?? '';

        // Fabric Meta Data (JSON): <input type="hidden" id="fabric-json-5" name="fabric-json">
        $this->inputHtmlAttributes['type'] = 'hidden';
        $this->inputHtmlAttributes[HTML_ATTR_ID] = $this->attributes[FE_HTML_ID];
        $this->inputHtmlAttributes[HTML_ATTR_NAME] = $this->htmlAttributes[HTML_ATTR_NAME];
        $valueEncode = rawUrlEncode($this->value);
        $this->inputHtmlAttributes['value'] = Support::escapeDoubleTick($valueEncode, ESCAPE_WITH_BACKSLASH);

        // Second hidden input for base64 image data
        $this->inputBase64HtmlAttributes['type'] = 'hidden';
        $this->inputBase64HtmlAttributes[HTML_ATTR_ID] = $this->fabricHtmlAttributes['data-base64-id'];
        $this->inputBase64HtmlAttributes[HTML_ATTR_NAME] = HTML_NAME_TARGET_IMAGE . $this->htmlAttributes[HTML_ATTR_NAME];
    }

    /**
     * Loads the image. Has to be called by the renderer class or any other class that needs the image.
     * Is not needed during form save!
     *
     * @return void
     * @throws \CodeException
     * @throws \UserFormException
     *
     */
    public function loadImage(): void {
        // Check if path empty or not readable
        if (!empty($this->attributes[FE_IMAGE_SOURCE])) {
            if (!is_readable($this->attributes[FE_IMAGE_SOURCE])) {
                throw new \UserFormException("Error reading file: " . $this->attributes[FE_IMAGE_SOURCE], ERROR_IO_READ_FILE);
            }
        }

        // Store source filename in SIP
        $field = HelperFormElement::AppendFormElementNameImageCut($this->attributes);
        $this->store->setVar($field, $this->value, STORE_SIP, false);

    }


    /**
     * @param string $pathFileName
     * @return string SIP encoded
     * @return bool|string
     * @throws \CodeException
     * @throws \UserFormException
     */
    private function fileToSipUrl($pathFileName): string {
        $param[DOWNLOAD_MODE] = DOWNLOAD_MODE_FILE;
        $param[SIP_DOWNLOAD_PARAMETER] = base64_encode(TOKEN_FILE . PARAM_TOKEN_DELIMITER . $pathFileName);
        return $this->store->getSipInstance()->queryStringToSip(Path::urlApi(API_DOWNLOAD_PHP) . '?' . KeyValueStringParser::unparse($param, '=', '&'), RETURN_URL);
    }
}