<?php
namespace Grav\Plugin;

use Grav\Common\Plugin;
use RocketTheme\Toolbox\Event\Event;

class MarkdownTypographyPlugin extends Plugin
{
    private array $thin_arrows;
    private array $thick_arrows;

    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            "onMarkdownInitialized" => ["onMarkdownInitialized", 0],
        ];
    }

    public function onMarkdownInitialized(Event $event)
    {
        $markdown = $event["markdown"];
        $config = $this->grav["config"]->get("plugins.".$this->name);


        // TODO: Once Grav requires Parsedown 1.8:
        // - $Element["name"] is no longer required, so remove redundant spans
        // - $Element["handler"] changes for extents with Markdown inside
        // - Do arrows starting with "<" work in that version?

        $arrows = $config["arrows"];

        // Enable arrows (before dashes to avoid conflict)
        if ($arrows["enabled"]) {
            $this->thin_arrows = explode(",", $arrows["thin_arrows"]) ?: ["→","←","⟶","⟵"];
            $this->thick_arrows = explode(",", $arrows["thick_arrows"]) ?: ["⇒","⇐","⟹","⟸"];

            // Add inline types
            $markdown->addInlineType("-", "TypographyArrowSingleRight");
            $markdown->addInlineType("=", "TypographyArrowDoubleRight");
            //$markdown->addInlineType(">", "TypographyArrowBothLeft");

            // Add function to handle inline type
            $markdown->inlineTypographyArrowSingleRight = function($excerpt) {
                if (preg_match("/^(--?>)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => strlen($matches[1]) == 2 ? $this->thin_arrows[0] : $this->thin_arrows[2 % count($this->thin_arrows)],  // single right arrow (long arrow: ⟶)
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
                    elseif (preg_match("/^(--?<)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => strlen($matches[1]) == 2 ? $this->thin_arrows[1] : $this->thin_arrows[3 % count($this->thin_arrows)],  // single left arrow (long arrow: ⟵)
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
            };


            $markdown->inlineTypographyArrowDoubleRight = function($excerpt) {
                if (preg_match("/^(==?>)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => strlen($matches[1]) == 2 ? $this->thick_arrows[0] : $this->thick_arrows[2 % count($this->thick_arrows)],  // fat right arrow
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
                    elseif (preg_match("/^(==?<)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => strlen($matches[1]) == 2 ? $this->thick_arrows[1] : $this->thick_arrows[3 % count($this->thick_arrows)],  // fat left arrow
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
            };

            // TODO: Doesn't seem to work at the moment!?
            /*$markdown->inlineTypographyArrowBothLeft = function($excerpt) {
                $this->grav["debugger"]->addMessage($excerpt);
                if (preg_match("/^(.--?)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => "←",  // thin left arrow
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
                    elseif (preg_match("/^(.==?)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => "⇦",  // fat left arrow
                            "attributes" => [
                                "class" => "typography-arrow"
                            ]
                        ),
                    );
                }
            };*/
        }

        // Enable dashes
        if ($config["dashes"]) {
            // Add inline type
            $markdown->addInlineType("-", "TypographyDash");

            // Add function to handle inline type
            $markdown->inlineTypographyDash = function($excerpt) {
                if (preg_match("/-/", $excerpt["text"], $matches) && preg_match("/ (-) /", $excerpt["context"], $matches))
                {
                    return array(
                        "extent" => 1,
                        "element" => array(
                            "name" => "span",
                            "text" => "–",  // en dash
                        ),
                    );
                } elseif (preg_match("/^(---)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => 3,
                        "element" => array(
                            "name" => "span",
                            "text" => "—",  // em dash
                        ),
                    );
                } elseif (preg_match("/^(--)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => 2,
                        "element" => array(
                            "name" => "span",
                            "text" => "–",  // en dash
                        ),
                    );
                }
            };
        }

        // Enable smart-quotes
        if ($config["smart_quotes"]) {
            // Add inline types
            $markdown->addInlineType('"', "TypographyDoubleQuote");
            $markdown->addInlineType("'", "TypographySingleQuote");

            // Add function to handle double quotes
            $markdown->inlineTypographyDoubleQuote = function($excerpt) {
                if (preg_match_all('/^"([\s\S]*?)"/', $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[0][0]),
                        "element" => array(
                            "name" => "span",
                            "text" => "“".$matches[1][0]."”",  // double-quotes
                            "handler" => "line",
                        ),
                    );
                }
                elseif (preg_match('/^\s?"/', $excerpt["context"], $matches))
                {
                    return array(
                        "extent" => strlen($excerpt["text"]),
                        "element" => array(
                            "name" => "span",
                            "text" => "“" . substr($excerpt["text"], 1),  // Lonely opening double-quotes
                            "handler" => "line",
                        ),
                    );
                }
            };

            // Add function to handle single quotes (+ apostrophes)
            $markdown->inlineTypographySingleQuote = function($excerpt) {
                if (preg_match("/\w".preg_quote($excerpt["text"], "/")."/", $excerpt["context"], $matches))
                {
                    return array(
                        "extent" => 1,
                        "element" => array(
                            "name" => "span",
                            "text" => "’",  // apostrophe
                        ),
                    );
                }
                elseif (preg_match_all("/^'([\s\S]*?)'(?=\W?)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[0][0]),
                        "element" => array(
                            "name" => "span",
                            "text" => "‘".$matches[1][0]."’",  // single-quotes
                            "handler" => "line",
                        ),
                    );
                }
                elseif (preg_match("/^(')|(\s')/", $excerpt["context"], $matches))
                {
                    return array(
                        "extent" => strlen($excerpt["text"]),
                        "element" => array(
                            "name" => "span",
                            "text" => "‘" . substr($excerpt["text"], 1),  // Lonely opening single-quote
                            "handler" => "line",
                        ),
                    );
                }
            };


            // Don't process inside of inline shortcode tags
            // This is a fix to stop quoted attributes getting broken when inline
            // Temporary tags are stripped by $this->stripTemporaryTags()
            // Once Parsedown 1.8 is used in Grav, the temp tags will be unnecessary
            $markdown->addInlineType("[", "TypographyShortcode");
            $markdown->inlineTypographyShortcode = function($excerpt) {
                if (preg_match("/\[[\s\S]+?((\".+?\")|(\'.+?\')|(\[.+?\])|([\s\S]+?))*?\]/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[0]),
                        "element" => array(
                            "name" => "temporary-tag",
                            "text" => $matches[0],
                        ),
                    );
                }
            };

            $this->enable(["onOutputGenerated" => ["stripTemporaryTags", 0]]);
        }

        // Enable ellipsis
        if ($config["ellipsis"]) {
            // Add inline type
            $markdown->addInlineType(".", "TypographyEllipsis");

            // Add function to handle inline type
            $markdown->inlineTypographyEllipsis = function($excerpt) {
                if (preg_match("/^(\.\.\.)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => 3,
                        "element" => array(
                            "name" => "span",
                            "text" => "…",  // ellipsis
                        ),
                    );
                }
            };
        }

        // Enable line breaks
        if ($config["line_breaks"]) {
            // Add inline type
            $markdown->addInlineType("\\", "TypographyLineBreaks");

            // Add function to handle inline type
            $markdown->inlineTypographyLineBreaks = function($excerpt) {
                if (preg_match("/\\\n/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => 1,
                        "element" => array(
                            "name" => "br",  // line break
                        ),
                    );
                }
            };
        }

        // Enable interrobang
        if ($config["interrobang"]) {
            // Add inline type
            $markdown->addInlineType("!", "TypographyInterrobang");

            // Add function to handle inline type
            $markdown->inlineTypographyInterrobang = function($excerpt) {
                if (preg_match("/^(!\?)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => 2,
                        "element" => array(
                            "name" => "span",
                            "text" => "‽",  // interrobang
                        ),
                    );
                }
            };
        }

        // Enable plus-minus
        if ($config["plus_minus"]) {
            // Add inline type
            $markdown->addInlineType("+", "TypographyPlusMinus");

            // Add function to handle inline type
            $markdown->inlineTypographyPlusMinus = function($excerpt) {
                if (preg_match("/^(\+\/?-)/", $excerpt["text"], $matches))
                {
                    return array(
                        "extent" => strlen($matches[1]),
                        "element" => array(
                            "name" => "span",
                            "text" => "±",  // plus-minus
                        ),
                    );
                }
            };
        }

        // Enable X Times
        if ($config["x_times"]) {
            // Add inline type
            $markdown->addInlineType("x", "TypographyXTimes");

            // Add function to handle inline type
            $markdown->inlineTypographyXTimes = function($excerpt) {
                if (
                    (substr($excerpt["text"], 0, 2) == "x ") and
                    (preg_match("/( ".preg_quote($excerpt["text"], "/").")/", $excerpt["context"], $matches))
                )
                {
                    return array(
                        "extent" => 1,
                        "element" => array(
                            "name" => "span",
                            "text" => "×",  // times symbol
                        ),
                    );
                }
            };
        }
    }

    public function stripTemporaryTags()
    {
        // Check that it's an HTML page, abort if not
        if ($this->grav["page"]->templateFormat() !== 'html') {
            return;
        }

        // Get the rendered content (HTML)
        $content = $this->grav->output;

        // Remove the temporary tags
        $content = str_replace("<temporary-tag>", "", $content);
        $content = str_replace("</temporary-tag>", "", $content);

        // Remove encoded temporary tags (eg. those generatred by [safe-email])
        $content = str_replace("&#60;&#116;&#101;&#109;&#112;&#111;&#114;&#97;&#114;&#121;&#45;&#116;&#97;&#103;&#62;", "", $content);
        $content = str_replace("&#60;&#47;&#116;&#101;&#109;&#112;&#111;&#114;&#97;&#114;&#121;&#45;&#116;&#97;&#103;&#62;", "", $content);

        // Rewrite the output
        $this->grav->output = $content;
    }
}
