import re

from docutils import nodes, utils
from docutils.parsers.rst import Directive, directives, roles
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import TextLexer, get_lexer_by_name

import pelican.settings as pys


class Pygments(Directive):
    """Source code syntax highlighting."""

    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    option_spec = {
        "anchorlinenos": directives.flag,
        "classprefix": directives.unchanged,
        "hl_lines": directives.unchanged,
        "lineanchors": directives.unchanged,
        "linenos": directives.unchanged,
        "linenospecial": directives.nonnegative_int,
        "linenostart": directives.nonnegative_int,
        "linenostep": directives.nonnegative_int,
        "lineseparator": directives.unchanged,
        "linespans": directives.unchanged,
        "nobackground": directives.flag,
        "nowrap": directives.flag,
        "tagsfile": directives.unchanged,
        "tagurlformat": directives.unchanged,
    }
    has_content = True

    def run(self):
        self.assert_has_content()
        try:
            lexer = get_lexer_by_name(self.arguments[0])
        except ValueError:
            # no lexer found - use the text one instead of an exception
            lexer = TextLexer()

        # Fetch the defaults
        if pys.PYGMENTS_RST_OPTIONS is not None:
            for k, v in pys.PYGMENTS_RST_OPTIONS.items():
                # Locally set options overrides the defaults
                if k not in self.options:
                    self.options[k] = v

        if "linenos" in self.options and self.options["linenos"] not in (
            "table",
            "inline",
        ):
            if self.options["linenos"] == "none":
                self.options.pop("linenos")
            else:
                self.options["linenos"] = "table"

        for flag in ("nowrap", "nobackground", "anchorlinenos"):
            if flag in self.options:
                self.options[flag] = True

        # noclasses should already default to False, but just in case...
        formatter = HtmlFormatter(noclasses=False, **self.options)
        parsed = highlight("\n".join(self.content), lexer, formatter)
        return [nodes.raw("", parsed, format="html")]


directives.register_directive("code-block", Pygments)
directives.register_directive("sourcecode", Pygments)


_abbr_re = re.compile(r"\((.*)\)$", re.DOTALL)


class abbreviation(nodes.Inline, nodes.TextElement):
    pass


def abbr_role(typ, rawtext, text, lineno, inliner, options=None, content=None):
    del typ, rawtext, lineno, inliner, options, content  # Unused arguments
    text = utils.unescape(text)
    m = _abbr_re.search(text)
    if m is None:
        return [abbreviation(text, text)], []
    abbr = text[: m.start()].strip()
    expl = m.group(1)
    return [abbreviation(abbr, abbr, explanation=expl)], []


roles.register_local_role("abbr", abbr_role)
