<?php

/***************************************************************
 *  Copyright notice
 *
 *  (c) sgalinski Internet Services (https://www.sgalinski.de)
 *
 *  All rights reserved
 *
 *  This script is part of the AY project. The AY project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

namespace SGalinski\SgVimeo\ViewHelpers;

use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;

/**
 * Class RenderSvgViewHelper
 *
 * @package SGalinski\SgVimeo\ViewHelpers
 */
class RenderSvgViewHelper extends AbstractTagBasedViewHelper {

	/**
	 * Name of the tag to be created by this view helper
	 *
	 * @var string
	 * @api
	 */
	protected $tagName = 'svg';

    protected $directoryPath = __DIR__ . '/../../Resources/Public/Icons/';

	/**
	 * Register the ViewHelper arguments
	 */
	public function initializeArguments(): void {
		parent::initializeArguments();
        $this->registerArgument('name', 'string', 'The SVG name', TRUE);
        $this->registerArgument('color', 'string', 'The fill color', TRUE);
        $this->registerArgument('id', 'string', 'The HTML id attribute', FALSE);
        $this->registerArgument('path', 'string', 'Path to the SVG file if not in default', FALSE);
        $this->registerArgument('class', 'string', 'The HTML class attribute', FALSE);
        $this->registerArgument('title', 'string', 'The HTML title attribute', FALSE);
        $this->registerArgument('width', 'string', 'The HTML width attribute', FALSE);
        $this->registerArgument('height', 'string', 'The HTML height attribute', FALSE);
        $this->registerArgument('style', 'string', 'Inline CSS styles', FALSE);
        $this->registerArgument('use', 'string', 'Inline CSS styles', FALSE);
	}


    /**
     * Render the SVG file as an inline SVG element.
     *
     * @return string The rendered SVG element
     * @throws \Exception
     */
    public function render(): string
    {
        $name = $this->arguments['name'];
        $path = $this->arguments['path'];
        $width = $this->arguments['width'];
        $height = $this->arguments['height'];
        $color = $this->arguments['color'];
        $id = $this->arguments['id'];
        $class = $this->arguments['class'];
        $style = $this->arguments['style'];
        $title = $this->arguments['title'];

        $src = $path ?? $this->directoryPath . '/' . $name .'.svg';


        // Get the content of the SVG file
        $content = file_get_contents($src);

        // Create a unique ID for the SVG element
        if (!$id) {
            $id = 'svg-' . md5($src . $width . $height . $color);
        }

        // Load the SVG into a SimpleXMLElement object
        $svg = new \SimpleXMLElement($content);

        // Set the attributes of the SVG element
        if ($width > 0) {
           $this->addOrReplaceAttribute($svg, 'width', $width);
        }

        if ($height > 0) {
            $this->addOrReplaceAttribute($svg, 'height', $height);
        }

        if (!empty($color)) {
            $this->setFill($svg, $color);
        }

        if ($style) {
            $this->addOrReplaceAttribute($svg, 'style', $style);
        }

        if ($class) {
            $this->addOrReplaceAttribute($svg, 'class', $class);
        }

        if ($title) {
            $this->addOrReplaceAttribute($svg,'title', $title);
        }

//        // Set the ID of the SVG element
//        $svg->attributes()->id = $id;


        // Extract the SVG contents
        $contents = $this->getContents($svg, true);

        // Check if the SVG has already been rendered and use the <use> tag if possible
        if ($this->viewHelperVariableContainer->exists('Vendor\\Extension\\ViewHelpers\\SvgViewHelper', 'svgIds')) {
            $svgIds = $this->viewHelperVariableContainer->get('Vendor\\Extension\\ViewHelpers\\SvgViewHelper', 'svgIds');
            if (isset($svgIds[$id])) {
                $use = $svg->addChild('use');
                $use->addAttribute('href', '#' . $id);
                return $svg->asXML();
            }
        } else {
            $svgIds = [];
        }

        // Add the unique ID to the list of rendered SVGs
        $svgIds[$id] = $id;
        $this->viewHelperVariableContainer->add('Vendor\\Extension\\ViewHelpers\\SvgViewHelper', 'svgIds', $svgIds);

        $contentsElement = new \SimpleXMLElement($contents);
        $group = $svg->addChild('g');
        /** @noinspection NullPointerExceptionInspection */
        $group->addAttribute('id', $id);
        $this->xmlAdopt($group, $contentsElement);

        return $svg->asXML();

        //Todo: we don't really want this, do we?
        if (!empty($width) && !empty($height)) {
        		$resultWithoutCssClass = str_replace($cssClass, '', $result);
        		$cssClasses = $this->processWrapperClasses($cssClassPlain);

        		return "<span $cssClasses style='width: $width; height: $height;'>$resultWithoutCssClass</span>";
        	}

        if (!empty($width)) {
            $resultWithoutCssClass = str_replace($cssClass, '', $result);
            $cssClasses = $this->processWrapperClasses($cssClassPlain);

            return "<span $cssClasses style='width: $width;'>$resultWithoutCssClass</span>";
        }

        if (!empty($height)) {
            $resultWithoutCssClass = str_replace($cssClass, '', $result);
            $cssClasses = $this->processWrapperClasses($cssClassPlain);

            return "<span $cssClasses style='height: $height;'>$resultWithoutCssClass</span>";
        }

        return $result;
    }

    /**
     * Processes the common SVG wrapper's classes
     *
     * @param string $cssClass
     * @return string
     */
    function processWrapperClasses(string $cssClass): string {
    	return "class='$cssClass svg-wrapper d-inline-flex justify-content-center align-items-center'";
    }

    /**
     * Processes the wrappers for the usage of the <use> SVG functionality
     *
     * @param string $svgContent
     * @param string $id
     * @param string $use
     * @return string
     */
    function processUseWrappers(string $svgContent, string $id, string $use): string {
    	$svgContent = "<g $id>$svgContent</g>";

    	if ($use) {
    		$svgContent = "<use href='#$use' />";
    	}

    	return $svgContent;
    }

    /**
    * Set the fill color of an SVG element.
    *
    * @param \SimpleXMLElement $element The SVG element
    * @param string $fill The fill color to set
    */
   protected function setFill(\SimpleXMLElement $element, string $fill): void
   {
       foreach ($element->children() as $child) {
           $this->setFill($child, $fill);
       }
       if (isset($element->attributes()->fill)) {
           $element->attributes()->fill = $fill;
       }
   }

    /**
     * Gets the contents of the SVG file
     *
     * @param \SimpleXMLElement $svg
     * @param bool $removeNode
     * @return string
     */
    private function getContents(\SimpleXMLElement $svg, bool $removeNode = false): string
    {
        $contents = '';
        foreach ($svg->children() as $child) {
            $contents .= $child->asXML() . "\n";
            if ($removeNode) {
                $dom = dom_import_simplexml($child);
                $dom->parentNode->removeChild($dom);
            }
        }

        return $contents;
    }

    /**
     * Inserts a new SimpleXMLElement at the given root
     *
     * @param \SimpleXMLElement $root
     * @param \SimpleXMLElement $newElement
     * @return void
     */
    private function xmlAdopt(\SimpleXMLElement $root, \SimpleXMLElement $newElement): void
    {
        $node = $root->addChild($newElement->getName(), (string) $newElement);

        foreach($newElement->attributes() as $attr => $value) {
            /** @noinspection NullPointerExceptionInspection */
            $node->addAttribute($attr, $value);
        }

        foreach($newElement->children() as $ch) {
            /** @noinspection NullPointerExceptionInspection */
            $this->xmlAdopt($node, $ch);
        }
    }

    /**
     * Adds or replaces an attribute
     *
     * @param \SimpleXMLElement $element
     * @param string $attributeName
     * @param string $attributeValue
     * @return void
     */
    private function addOrReplaceAttribute(\SimpleXMLElement $element,
        string $attributeName,
        string $attributeValue): void
    {
        if (isset($element[$attributeName])) {
            $element[$attributeName] = $attributeValue; // Replace existing attribute value
        } else {
            $element->addAttribute($attributeName, $attributeValue); // Add new attribute
        }
    }
}