<?php declare(strict_types=1);
namespace Armin\Vieweg\ViewHelpers;

use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;

/**
 * Font Awesome SVG ViewHelper
 *
 * Usage examples:
 *
 * <av:fa icon="fab typo3" />
 * <av:fa icon="fas file" /> == <av:fa icon="file" />
 * <av:fa icon="far file" />
 */
class FaViewHelper extends AbstractViewHelper
{
    protected $escapeOutput = false;

    protected static $cache = [];

    public function initializeArguments()
    {
        $this->registerArgument('icon', 'string', 'Icon name', true);
        $this->registerArgument('size', 'string', 'Icon size in pixel', false, '');
        $this->registerArgument('class', 'string', 'Optional class name(s)', false, '');
        $this->registerArgument('color', 'string', 'Optional color', false, '');
    }

    public static function renderStatic(
        array $arguments,
        \Closure $renderChildrenClosure,
        RenderingContextInterface $renderingContext
    ) {
        // Prepare requested icon (type and icon (name))
        $iconParts = GeneralUtility::trimExplode(' ', $arguments['icon'], true);
        $type = 'solid';
        if (count($iconParts) === 2) {
            $firstPart = reset($iconParts);
            if ($firstPart === 'far') {
                $type = 'regular';
            } elseif ($firstPart === 'fab') {
                $type = 'brands';
            } elseif ($firstPart === 'fas') {
                $type = 'solid';
            } else {
                throw new \InvalidArgumentException(
                    'Font Awesome icon prefix "' . $firstPart . '" not allowed! Allowed are: "far", "fas" or "fab"'
                );
            }
            $icon = end($iconParts);
        } elseif (count($iconParts) === 1) {
            $icon = reset($iconParts);
        } else {
            throw new \InvalidArgumentException(
                'Invalid icon name given. Valid name would be: "fas fa-file" or "fa-file" or just "file"'
            );
        }

        if (strpos($icon, 'fa-') === 0) {
            $icon = substr($icon, 3);
        }

        // Check if icon exists
        $path = Environment::getProjectPath() . '/vendor/fortawesome/font-awesome/svgs/';
        $path .= $type . '/';
        $path .= $icon . '.svg';

        if (!file_exists($path)) {
            throw new \RuntimeException('Given Font Awesome Icon "' . $arguments['icon'] . '" not found!');
        }

        // Prepare view helper arguments
        $size = (string) $arguments['size'];
        $color = (string) $arguments['color'];
        $class = 'svg-icon';
        if (!empty($arguments['class'])) {
            $class .= ' ' . $arguments['class'];
        }

        // If the same icon is requested a second time, use a reference to symbol instead
        if (array_key_exists($path, self::$cache)) {
            $id = self::$cache[$path];
            return self::buildSvgSymbolReference($id, $size, $class, $color);
        }

        // Create and cache symbol
        return self::createSvgSymbol($type, $icon, $path, $size, $class, $color);
    }

    protected static function buildSvgSymbolReference(string $id, string $size, string $class, string $color): string
    {
        if (!empty($size)) {
            $size = ' width="' . $size . '" height="' . $size . '" ';
        }
        if (!empty($color)) {
            $color = ' fill="' . $color . '"';
        }
        return '<svg' . $size . $color . ' class="' . $class . '">'
               . '<use xlink:href="#' . $id . '"></use>' .
               '</svg>';
    }

    /**
     * @param string $type
     * @param string $icon
     * @param string $path
     * @param string $size
     * @param string $class
     * @param string $color
     * @return string
     */
    protected static function createSvgSymbol(
        string $type,
        string $icon,
        string $path,
        string $size,
        string $class,
        string $color
    ) : string {

        $svgContents = file_get_contents($path);
        $svgDocument = new \DOMDocument();
        $svgDocument->loadXML($svgContents);

        // Used for caching and symbol identifier
        $id = 'fa-' . $type . '-' . $icon;

        // Create the symbol
        $symbolDocument = new \DOMDocument();
        $symbol = $symbolDocument->createElement('symbol');
        $symbol->setAttribute('id', $id);
        $symbol->setAttribute('viewBox', $svgDocument->documentElement->getAttribute('viewBox'));
        $symbolDocument->appendChild($symbol);

        // Get paths of font awesome SVG
        foreach ($svgDocument->documentElement->childNodes as $svgpath) {
            $iconPathsFragment = $symbolDocument->createDocumentFragment();
            $iconPathsFragment->appendXML($svgDocument->saveXML($svgpath));
            $symbol->appendChild($iconPathsFragment);
        }

        // Prepare symbol output
        $result = '<svg class="d-none">';
        foreach ($symbolDocument->childNodes as $childNode) {
            $result .= $symbolDocument->saveXML($childNode);
        }
        $result .= '</svg>';

        // Directly append a reference to this symbol (which is never displayed)
        $result .= self::buildSvgSymbolReference($id, (string)$size, $class, $color);

        // Add cache item and return the output
        self::$cache[$path] = $id;
        return $result;
    }
}