<?php

/**
 * This file contains \QUI\Animate\Events
 */

namespace QUI\Animate;

use JsonException;
use QUI;
use QUI\Exception;
use QUI\Projects\Project;
use QUI\Template;

use function is_string;
use function json_encode;
use function trim;

use const JSON_HEX_AMP;
use const JSON_HEX_APOS;
use const JSON_HEX_QUOT;
use const JSON_HEX_TAG;
use const JSON_THROW_ON_ERROR;

/**
 * Class Events
 *
 * @package quiqqer/animate
 */
class Events
{
    private const JSON_FLAGS = JSON_THROW_ON_ERROR
        | JSON_HEX_TAG
        | JSON_HEX_AMP
        | JSON_HEX_APOS
        | JSON_HEX_QUOT;

    /**
     * @var array{fadeIn: string, bottom: string, left: string, right: string}
     */
    private const DEFAULT_SCROLL_SELECTORS = [
        'fadeIn' => '.fade-in',
        'bottom' => '.fadein-from-bottom, .fadeIn-from-bottom, .fade-in-from-bottom',
        'left' => '.fadein-from-left, .fadeIn-from-left, .fade-in-from-left',
        'right' => '.fadein-from-right, .fadeIn-from-right, .fade-in-from-right'
    ];

    /**
     * Event : on template get header
     *
     * @throws Exception
     * @throws JsonException
     */
    public static function onTemplateGetHeader(Template $Template): void
    {
        $Project = QUI::getRewrite()->getProject();

        if (!$Project) {
            return;
        }

        self::addAnimationLibrary($Template, $Project);
        self::addScrollAnimations($Template, $Project);
    }

    private static function addAnimationLibrary(Template $Template, Project $Project): void
    {
        if ($Project->getConfig('animate.general.enableAnimationLibrary')) {
            $Template->extendHeaderWithCSSFile(URL_OPT_DIR . 'quiqqer/animate/bin/animate.min.css');
        }
    }

    /**
     * @throws JsonException
     */
    private static function addScrollAnimations(Template $Template, Project $Project): void
    {
        if (!$Project->getConfig('animate.scroll.enable')) {
            return;
        }

        $Template->extendHeaderWithCSSFile(URL_OPT_DIR . 'quiqqer/animate/bin/css/animate-scroll.css');
        $Template->extendFooterWithJavaScriptFile(URL_OPT_DIR . 'quiqqer/animate/bin/javascript/animate-scroll.js');
        $Template->extendHeader(
            self::getConfigurationScript(
                self::getScrollSelectors($Project),
                (bool)$Project->getConfig('animate.scroll.skipAnimationIfVisible')
            )
        );

        // Never write configurable selectors into a CSS context.
        $Template->extendHeader(self::getInitialStyles());
    }

    /**
     * @return array{fadeIn: string, bottom: string, left: string, right: string}
     */
    private static function getScrollSelectors(Project $Project): array
    {
        return [
            'fadeIn' => self::getSelectorList(
                self::DEFAULT_SCROLL_SELECTORS['fadeIn'],
                $Project->getConfig('animate.scroll.fadeIn')
            ),
            'bottom' => self::getSelectorList(
                self::DEFAULT_SCROLL_SELECTORS['bottom'],
                $Project->getConfig('animate.scroll.fadeinFromDown')
            ),
            'left' => self::getSelectorList(
                self::DEFAULT_SCROLL_SELECTORS['left'],
                $Project->getConfig('animate.scroll.fadeinFromLeft')
            ),
            'right' => self::getSelectorList(
                self::DEFAULT_SCROLL_SELECTORS['right'],
                $Project->getConfig('animate.scroll.fadeinFromRight')
            )
        ];
    }

    private static function getSelectorList(string $defaultSelectors, mixed $configuredSelectors): string
    {
        if (!is_string($configuredSelectors)) {
            return $defaultSelectors;
        }

        $configuredSelectors = trim($configuredSelectors);

        if ($configuredSelectors === '') {
            return $defaultSelectors;
        }

        return $defaultSelectors . ', ' . $configuredSelectors;
    }

    /**
     * @param array{fadeIn: string, bottom: string, left: string, right: string} $selectors
     *
     * @throws JsonException
     */
    private static function getConfigurationScript(array $selectors, bool $skipAnimationIfVisible): string
    {
        $script = '<script type="text/javascript" data-no-cache="1">';
        $script .= 'const QUI_ANIMATE_SCROLL_EFFECTS = ' . self::encodeForInlineScript($selectors) . ';';
        $script .= 'const QUI_ANIMATE_SKIP_ANIMATION_IF_VISIBLE = ';
        $script .= self::encodeForInlineScript($skipAnimationIfVisible ? 1 : 0) . ';';

        return $script . '</script>';
    }

    /**
     * @throws JsonException
     */
    private static function encodeForInlineScript(mixed $value): string
    {
        return json_encode($value, self::JSON_FLAGS);
    }

    private static function getInitialStyles(): string
    {
        $css = '';

        foreach (self::DEFAULT_SCROLL_SELECTORS as $selectorList) {
            $css .= '<style>' . $selectorList . '{opacity: 0}</style>';
        }

        return $css;
    }
}
