<?php

namespace Medienreaktor\Energize\ViewHelpers;

use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Extbase\Service\ImageService;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder;

/**
 * Resizes a given image (if required) and renders a source tag for picture elements
 */
class ImageSourceViewHelper extends AbstractTagBasedViewHelper
{
    /**
     * @var ImageService
     */
    protected $imageService;

    /**
     * @param ImageService $imageService
     */

    public function __construct(ImageService $imageService)
    {
        parent::__construct();
        $this->imageService = $imageService;
        $this->tag = new TagBuilder('source');
    }

    /**
     * Initialize arguments.
     */
    public function initializeArguments(): void
    {
        parent::initializeArguments();

        $this->registerArgument('src', 'string', 'Image source', false, '');
        $this->registerArgument('image', 'object', 'A File object', false, null);
        $this->registerArgument('treatIdAsReference', 'bool', 'Treat src as reference', false, false);
        $this->registerArgument('crop', 'string', 'Crop variant', false, null);
        $this->registerArgument('cropVariant', 'string', 'Crop variant', false, 'default');
        $this->registerArgument('width', 'string', 'Width', false, null);
        $this->registerArgument('height', 'string', 'Height', false, null);
        $this->registerArgument('minWidth', 'string', 'Minimum width', false, null);
        $this->registerArgument('minHeight', 'string', 'Minimum height', false, null);
        $this->registerArgument('maxWidth', 'string', 'Maximum width', false, null);
        $this->registerArgument('maxHeight', 'string', 'Maximum height', false, null);
        $this->registerArgument('absolute', 'bool', 'Absolute URI', false, false);
        $this->registerArgument('media', 'string', 'This source\'s media query');
        $this->registerArgument('hdpi', 'bool', 'Enable output of alternative HDPi images variants', false, true);
    }

    /**
     * Render the view helper.
     *
     * @return string Rendered tag
     * @throws Exception
     */
    public function render(): string
    {
        $src = (string)$this->arguments['src'];
        if (($src === '' && $this->arguments['image'] === null) || ($src !== '' && $this->arguments['image'] !== null)) {
            throw new Exception('You must either specify a string src or a File object.', 1382284106);
        }

        try {
            $image = $this->imageService->getImage($this->arguments['src'], $this->arguments['image'], $this->arguments['treatIdAsReference']);
            $cropString = $this->arguments['crop'];
            if ($cropString === null && $image->hasProperty('crop') && $image->getProperty('crop')) {
                $cropString = $image->getProperty('crop');
            }
            $cropVariantCollection = CropVariantCollection::create((string)$cropString);
            $cropVariant = $this->arguments['cropVariant'] ?: 'default';
            $cropArea = $cropVariantCollection->getCropArea($cropVariant);
            $processingInstructions = [
                'width' => $this->arguments['width'],
                'height' => $this->arguments['height'],
                'minWidth' => $this->arguments['minWidth'],
                'minHeight' => $this->arguments['minHeight'],
                'maxWidth' => $this->arguments['maxWidth'],
                'maxHeight' => $this->arguments['maxHeight'],
                'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
            ];
            $processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);
            $imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);

            if (!$this->tag->hasAttribute('data-focus-area')) {
                $focusArea = $cropVariantCollection->getFocusArea($cropVariant);
                if (!$focusArea->isEmpty()) {
                    $this->tag->addAttribute('data-focus-area', $focusArea->makeAbsoluteBasedOnFile($image));
                }
            }

            $srcset = $imageUri . " " . $processedImage->getProperty('width') . "w";

            /*
             * Additional code to get HDPI images delivered by default
             */
            if ($this->arguments['hdpi']) {
                try {
                    $hdpi2ProcessingInstructions = $processingInstructions;
                    foreach (['width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight'] as $dimension) {
                        if ($hdpi2ProcessingInstructions[$dimension]) {
                            $hdpi2ProcessingInstructions[$dimension] *= 2;
                        }
                    }
                    $hdpi2ProcessedImage = $this->imageService->applyProcessingInstructions($image, $hdpi2ProcessingInstructions);
                    $hdpi2ImageUri = $this->imageService->getImageUri($hdpi2ProcessedImage, $this->arguments['absolute']);

                    $srcset .= ", " . $hdpi2ImageUri . " " . $hdpi2ProcessedImage->getProperty('width') . "w";
                } catch (ResourceDoesNotExistException|\UnexpectedValueException|\RuntimeException|\InvalidArgumentException $e) {
                    // Handle exceptions
                }
            }

            $this->tag->addAttribute("srcset", $srcset);
            $this->tag->addAttribute("media", $this->arguments["media"]);
            $this->tag->setTagName("source");

        } catch (ResourceDoesNotExistException|\UnexpectedValueException|\RuntimeException|\InvalidArgumentException $e) {
            // Handle exceptions
        }

        return $this->tag->render();
    }
}
