<?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 the respective img tag
 */
class ImageViewHelper extends AbstractTagBasedViewHelper
{
    /**
     * @var ImageService
     */
    protected $imageService;

    /**
     * @param ImageService $imageService
     */
    public function __construct(ImageService $imageService)
    {
        parent::__construct();
        $this->imageService = $imageService;
        $this->tag = new TagBuilder('img');
    }

    /**
     * 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('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));
                }
            }
            $this->tag->addAttribute('src', $imageUri);
            $this->tag->addAttribute('width', $processedImage->getProperty('width'));
            $this->tag->addAttribute('height', $processedImage->getProperty('height'));

            $alt = $image->getProperty('alternative');
            $title = $image->getProperty('title');

            // The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
            if (empty($this->arguments['alt'])) {
                $this->tag->addAttribute('alt', $alt);
            }
            if (empty($this->arguments['title']) && $title) {
                $this->tag->addAttribute('title', $title);
            }
        } catch (ResourceDoesNotExistException|\UnexpectedValueException|\RuntimeException|\InvalidArgumentException $e) {
            // Handle exceptions
        }

        /*
         * 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']);

                // Add srcset attribute to tag
                $this->tag->addAttribute('srcset', $imageUri . ' ' . $processedImage->getProperty('width') . 'w, ' . $hdpi2ImageUri . ' ' . $hdpi2ProcessedImage->getProperty('width') . 'w');
            } catch (ResourceDoesNotExistException|\UnexpectedValueException|\RuntimeException|\InvalidArgumentException $e) {
                // Handle exceptions
            }
        }
        $this->tag->setTagName("img");

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