<?php

namespace Internetgalerie\IgDatapoolFe\ViewHelpers\Form;

use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
use TYPO3\CMS\Extbase\Property\PropertyMapper;
use Internetgalerie\IgDatapoolFe\Utility\FormUtility;
use Iterator;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Domain\Model\File;
use Exception;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;

/**
 * Renders an AJAX File Field.
 */
abstract class AbstractFileViewHelper extends AbstractFormFieldViewHelper
{
    /**
     * @var HashService
     */
    protected $hashService;

    /**
     * @var PropertyMapper
     */
    protected $propertyMapper;


    /**
     * @var FormUtility
     */
    protected $formUtility;

    protected $dpObject = 'ObjectStorage';

    protected $debug = false;

    protected $dragAndDrop = false;

    /**
     * Options for the AJAX Uploader
     *
     * @see https://github.com/LPology/Simple-Ajax-Uploader
     */
    protected $options = [];

    // Labels Tranlations - todo
    protected $labels = [];

    // Classes dor container
    protected $cssClasses = ['dp-file'];

    // current value
    protected $value = null;

    // max Files to Upload
    protected $maxUploads = 1;

    /**
     * @param HashService $hashService
     */
    public function injectHashService(HashService $hashService): void
    {
        $this->hashService = $hashService;
    }

    /**
     * @param PropertyMapper $propertyMapper
     */
    public function injectPropertyMapper(PropertyMapper $propertyMapper): void
    {
        $this->propertyMapper = $propertyMapper;
    }

    /**
     * @param FormUtility $formUtility
     */
    public function injectFormUtility(FormUtility $formUtility): void
    {
        $this->formUtility = $formUtility;
    }

    /**
     * Initialize the arguments.
     *
     * @return void
     * @api
     */
    public function initializeArguments(): void
    {
        parent::initializeArguments();
        $this->registerArgument('allowedFileExt', 'string', 'List of allowed file types');
        $this->registerArgument('acceptMimeTypes', 'string', 'Mime Types, i.e. images/*. only these will be shown in the upload selector. accept="" attrib. see http://www.iana.org/assignments/media-types/media-types.xhtml.');
        $this->registerArgument('maxFileSize', 'string', 'Maximum file size in 10KB, 10MB, etc..');
        $this->registerArgument('folder', 'string', 'Storage folder of the uploaded files');

        $this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this view helper', false, 'f3-form-error');
        $this->registerArgument('as', 'string', 'Name für Daten bei eigener Gestaltung', false);
        $this->registerArgument('class', 'string', 'CSS class', false, '');
        $this->registerArgument('uploadLabel', 'string', 'Label/Text for upload button', false, 'Datei auswählen...');
        $this->registerArgument('uploadClass', 'string', 'CSS class upload button', false, '');
        $this->registerArgument('layout', 'string', 'Layout', false, 'image');

        $this->registerArgument('dropzone', 'string', 'ID for external dropzone (default: id of this file upload area)', false);
        $this->registerArgument('dragAndDrop', 'boolean', 'Activate Drag and Drop', false, false);
        $this->registerArgument('dragClass', 'string', 'CSS class to be applied to drop zone element when a file is dragged over.', false, 'dp-dropzone-active');

        $this->registerArgument('maxSize', 'int', 'Maximum allowed file size (in kilobytes). 0= no limit / Only works in browsers that support File API.', false, 0);
        $this->registerArgument('ajaxActionPageType', 'int', 'Page type for ajax requests', false, 0);
    }

    public function renderFile($value, $index = null)
    {
        // eigene Gestatlung
        if ($this->arguments['as']) {
            $templateVariableContainer = $this->renderingContext->getTemplateVariableContainer();
            $templateVariableContainer->add($this->arguments['as'], $value->getOriginalResource());
            $out = $this->renderChildren();
            $templateVariableContainer->remove($this->arguments['as']);
            return $out;
        }
        return $this->formUtility->renderFile($value, $index);
    }

    protected function renderUploader()
    {
        return $this->formUtility->renderUploader($this->arguments['uploadLabel'], $this->arguments['uploadClass']);
    }

    protected function renderCurrentFiles($value)
    {
        $html = '';
        if (($value !== null && !($value instanceof Iterator)) || (($value instanceof Iterator) && $value->count() > 0)) {
            if ($value instanceof Iterator) {
                // File lists are not implemented yet!
                //throw new \Exception('Upload doesn\'t support StorageObjects/Arrays at the moment. please use object.myfield.0)');
                $index = 0;
                foreach ($value as $v) {
                    $html .= $this->renderFile($v, $index);
                    $index++;
                }
                //$value->rewind();
                // $value = $value->current();
            } else {
                //here.
                $html = $this->renderFile($value);
            }
        }
        return $html;
    }
    protected function countCurrentFiles($value)
    {
        $count = 0;
        if (($value !== null && !($value instanceof Iterator)) || (($value instanceof Iterator) && $value->count() > 0)) {
            if ($value instanceof Iterator) {
                // File lists are not implemented yet!
                //throw new \Exception('Upload doesn\'t support StorageObjects/Arrays at the moment. please use object.myfield.0)');
                foreach ($value as $v) {
                    $count++;
                }
            } else {
                $count++;
            }
        }
        return $count;
    }

    protected function renderInputFields($currentValue, $options)
    {
        $this->registerFieldNameForFormTokenGeneration($this->getName());
        if ($currentValue instanceof ObjectStorage) {
            $content = '<input type="hidden" class="dpObject" name="' . $this->getName() . '[__dpObject]" value="ObjectStorage" />';

            $index = 0;

            foreach ($currentValue as $value) {
                $content .= $this->formUtility->renderFileInputFields($this->getName(), $index, $value);
                $index++;
            }
            $content .= $this->formUtility->renderFileInputFields($this->getName(), $index, null); //new
            return $content;
        }
        /*
        elseif( $currentValue instanceof \TYPO3\CMS\Core\Resource\File) {
        return $this->formUtility->renderFileInputFields($this->getName(), null, $currentValue, $options);
        }*/

        return $this->formUtility->renderFileInputFields($this->getName(), null, $currentValue);
    }

    /**
     * Returns a previously uploaded resource.
     * If errors occurred during property mapping for this property, NULL is returned
     *
     * @return File
     */
    protected function getUploadedResource()
    {
        if ($this->getMappingResultsForProperty()->hasErrors()) {
            return null;
        }
        $resource = $this->getValueAttribute();

        // do we get a file resource?
        if ($resource instanceof File) {
            // in case it doesnt exist or there is any problem, return NULL, this is a bit ugly
            try {
                $resource->getOriginalResource()->getOriginalFile()->getContents();
            } catch (Exception) {
                return null;
            }
            return $resource;
        }
        //maybe we already converted it before?
        if ($resource instanceof ObjectStorage) {
            // in case it doesnt exist or there is any problem, return NULL, this is a bit ugly
            return $resource;
        }
        return $this->propertyMapper->convert($resource, File::class);
    }

    protected function prepareOptions()
    {
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
        $uriBuilder->reset()
            ->setRequest($this->renderingContext->getRequest());

        if (isset($this->arguments['ajaxActionPageType']) && $this->arguments['ajaxActionPageType'] > 0) {
            $uriBuilder->setTargetPageType($this->arguments['ajaxActionPageType']);
        }

        $this->options['url'] = $uriBuilder->uriFor('ajaxUpload');
        $this->options['name'] = 'uploadFile';
        $this->options['layout'] = $this->arguments['layout'];

        if ($this->hasArgument('maxFileSize')) {
            // in KB
            $this->options['maxSize'] = GeneralUtility::getBytesFromSizeMeasurement(rtrim((string) $this->arguments['maxFileSize'], 'b')) / 1024;
        }

        if ($this->hasArgument('minImageSize')) {
            // in mp
            $this->options['minImageSize'] = $this->arguments['minImageSize'];
        }

        if ($this->hasArgument('allowedFileExt')) {
            $this->options['allowedExtensions'] = GeneralUtility::trimExplode(',', $this->arguments['allowedFileExt'], true);
        }

        if ($this->hasArgument('acceptMimeTypes')) {
            $this->options['accept'] = $this->arguments['acceptMimeTypes'];
        }
        if ($this->hasArgument('folder')) {
            $this->options['folder'] = $this->arguments['folder'];
        }
        //$name = $this->getName();
        $this->options['dropzoneId'] = 'dp-dropzone-' . $this->arguments['property'] ?? str_replcae(['[', ']', '.'], '-', $this->getName());
        $this->dragAndDrop = $this->hasArgument('dragAndDrop') ? $this->arguments['dragAndDrop'] : false;
        if ($this->dragAndDrop) {
            if ($this->hasArgument('dropzone') && $this->arguments['dropzone']) {
                $this->options['dropzone'] = $this->arguments['dropzone'];
            }
            $this->options['dropzone'] = $this->options['dropzoneId'];
        }
        if ($this->hasArgument('dragClass')) {
            $this->options['dragClass'] = $this->arguments['dragClass'];
        }

        if ($this->hasArgument('maxUploads')) {
            $this->maxUploads = (int) $this->arguments['maxUploads'];
        }
        $this->options['maxUploads'] = $this->maxUploads;
    }

    protected function renderHtml($value)
    {
        return '<div class="' . implode(' ', $this->cssClasses) . '" data-labels="' . htmlentities(json_encode($this->labels)) . '" data-options="' . htmlentities(json_encode($this->options)) . '"' . ($this->dragAndDrop ? ' id="' . $this->options['dropzoneId'] . '"' : '') . '>
        <div class="dp-file-list dp-file-list-layout-' . $this->options['layout'] . '">' . $this->renderCurrentFiles($value) . '</div>
        <div class="dp-file-new"' . ($this->countCurrentFiles($value) >= $this->maxUploads ? ' style="display:none"' : '') . '>' . $this->renderUploader() . '</div>
        <div class="dp-file-progress" style="display:none;"><div class="dp-file-progressBarWrapper"></div><div class="dp-file-percentage">0%</div></div>
        <div class="dp-file-hidden-fields" data-name="' . $this->getName() . '" data-max-uploads="' . $this->maxUploads . '" style="display:none;">' . $this->renderInputFields($value, $this->options) . '</div>
        </div>';
        // . $hiddenFields
        //$output .='<div class="dp-dropzone" id="' . $this->options['dropzoneId'] . '">DRAG and DROP</div>';
    }
}
