"""
    pygments.lexers.ul4
    ~~~~~~~~~~~~~~~~~~~

    Lexer for the UL4 templating language.

    More information: https://python.livinglogic.de/UL4.html

    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import re

from pygments.lexer import RegexLexer, DelegatingLexer, bygroups, words, include
from pygments.token import Comment, Text, Keyword, String, Number, Literal, Name, Other, Operator
from pygments.lexers.web import HtmlLexer, XmlLexer, CssLexer, JavascriptLexer
from pygments.lexers.python import PythonLexer

__all__ = ["UL4Lexer", "HTMLUL4Lexer", "XMLUL4Lexer", "CSSUL4Lexer", "JavascriptUL4Lexer", "PythonUL4Lexer"]


class UL4Lexer(RegexLexer):
    """
    Generic lexer for UL4.

    .. versionadded:: 2.12
    """

    flags = re.MULTILINE | re.DOTALL

    name = "UL4"
    aliases = ["ul4"]
    filenames = ["*.ul4"]

    tokens = {
        "root": [
            (
                # Template header without name:
                # ``<?ul4?>``
                r"(<\?)(\s*)(ul4)(\s*)(\?>)",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword, Text.Whitespace, Comment.Preproc),
            ),
            (
                # Template header with name (potentially followed by the signature):
                # ``<?ul4 foo(bar=42)?>``
                r"(<\?)(\s*)(ul4)(\s*)([a-zA-Z_][a-zA-Z_0-9]*)?",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword, Text.Whitespace, Name.Function),
                "ul4",  # Switch to "expression" mode
            ),
            (
                # Comment:
                # ``<?note foobar?>``
                r"<\?\s*note\s.*?\?>",
                Comment,
            ),
            (
                # Template documentation:
                # ``<?doc foobar?>``
                r"<\?\s*doc\s.*?\?>",
                String.Doc,
            ),
            (
                # ``<?ignore?>`` tag for commenting out code:
                # ``<?ignore?>...<?end ignore?>``
                r"<\?\s*ignore\s*\?>",
                Comment,
                "ignore",  # Switch to "ignore" mode
            ),
            (
                # ``<?def?>`` tag for defining local templates
                # ``<?def foo(bar=42)?>...<?end def?>``
                r"(<\?)(\s*)(def)(\s*)([a-zA-Z_][a-zA-Z_0-9]*)?",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword, Text.Whitespace, Name.Function),
                "ul4",  # Switch to "expression" mode
            ),
            (
                # The rest of the supported tags
                r"(<\?)(\s*)(printx|print|for|if|elif|else|while|code|renderblocks?|render)\b",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword),
                "ul4",  # Switch to "expression" mode
            ),
            (
                # ``<?end?>`` tag for ending ``<?def?>``, ``<?for?>``,
                # ``<?if?>``, ``<?while?>``, ``<?renderblock?>`` and
                # ``<?renderblocks?>`` blocks.
                r"(<\?)(\s*)(end)\b",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword),
                "end",  # Switch to "end tag" mode
            ),
            (
                # ``<?whitespace?>`` tag for configuring whitespace handlng
                r"(<\?)(\s*)(whitespace)\b",
                bygroups(Comment.Preproc, Text.Whitespace, Keyword),
                "whitespace",  # Switch to "whitespace" mode
            ),
            # Plain text
            (r"[^<]+", Other),
            (r"<", Other),
        ],
        # Ignore mode ignores everything upto the matching ``<?end ignore?>`` tag
        "ignore": [
            # Nested ``<?ignore?>`` tag
            (r"<\?\s*ignore\s*\?>", Comment, "#push"),
            # ``<?end ignore?>`` tag
            (r"<\?\s*end\s+ignore\s*\?>", Comment, "#pop"),
            # Everything else
            (r"[^<]+", Comment),
            (r".", Comment),
        ],
        # UL4 expressions
        "ul4": [
            # End the tag
            (r"\?>", Comment.Preproc, "#pop"),
            # Start triple quoted string constant
            ("'''", String, "string13"),
            ('"""', String, "string23"),
            # Start single quoted string constant
            ("'", String, "string1"),
            ('"', String, "string2"),
            # Floating point number
            (r"\d+\.\d*([eE][+-]?\d+)?", Number.Float),
            (r"\.\d+([eE][+-]?\d+)?", Number.Float),
            (r"\d+[eE][+-]?\d+", Number.Float),
            # Binary integer: ``0b101010``
            (r"0[bB][01]+", Number.Bin),
            # Octal integer: ``0o52``
            (r"0[oO][0-7]+", Number.Oct),
            # Hexadecimal integer: ``0x2a``
            (r"0[xX][0-9a-fA-F]+", Number.Hex),
            # Date or datetime: ``@(2000-02-29)``/``@(2000-02-29T12:34:56.987654)``
            (r"@\(\d\d\d\d-\d\d-\d\d(T(\d\d:\d\d(:\d\d(\.\d{6})?)?)?)?\)", Literal.Date),
            # Color: ``#fff``, ``#fff8f0`` etc.
            (r"#[0-9a-fA-F]{8}", Literal.Color),
            (r"#[0-9a-fA-F]{6}", Literal.Color),
            (r"#[0-9a-fA-F]{3,4}", Literal.Color),
            # Decimal integer: ``42``
            (r"\d+", Number.Integer),
            # Operators
            (r"//|==|!=|>=|<=|<<|>>|\+=|-=|\*=|/=|//=|<<=|>>=|&=|\|=|^=|=|[\[\]{},:*/().~%&|<>^+-]", Operator),
            # Keywords
            (words(("for", "in", "if", "else", "not", "is", "and", "or"), suffix=r"\b"), Keyword),
            # Builtin constants
            (words(("None", "False", "True"), suffix=r"\b"), Keyword.Constant),
            # Variable names
            (r"[a-zA-Z_][a-zA-Z0-9_]*", Name),
            # Whitespace
            (r"\s+", Text.Whitespace),
        ],
        # ``<?end ...?>`` tag for closing the last open block
        "end": [
            (r"\?>", Comment.Preproc, "#pop"),
            (words(("for", "if", "def", "while", "renderblock", "renderblocks"), suffix=r"\b"), Keyword),
            (r"\s+", Text),
        ],
        # Content of the ``<?whitespace ...?>`` tag:
        # ``keep``, ``strip`` or ``smart``
        "whitespace": [
            (r"\?>", Comment.Preproc, "#pop"),
            (words(("keep", "strip", "smart"), suffix=r"\b"), Comment.Preproc),
            (r"\s+", Text.Whitespace),
        ],
        # Inside a string constant
        "stringescapes": [
            (r"""\\[\\'"abtnfr]""", String.Escape),
            (r"\\x[0-9a-fA-F]{2}", String.Escape),
            (r"\\u[0-9a-fA-F]{4}", String.Escape),
            (r"\\U[0-9a-fA-F]{8}", String.Escape),
        ],
        # Inside a triple quoted string started with ``'''``
        "string13": [
            (r"'''", String, "#pop"),
            include("stringescapes"),
            (r"[^\\']+", String),
            (r".", String),
        ],
        # Inside a triple quoted string started with ``"""``
        "string23": [
            (r'"""', String, "#pop"),
            include("stringescapes"),
            (r'[^\\"]+', String),
            (r".", String),
        ],
        # Inside a single quoted string started with ``'``
        "string1": [
            (r"'", String, "#pop"),
            include("stringescapes"),
            (r"[^\\']+", String),
            (r".", String),
        ],
        # Inside a single quoted string started with ``"``
        "string2": [
            (r'"', String, "#pop"),
            include("stringescapes"),
            (r'[^\\"]+', String),
            (r".", String),
        ],
    }


class HTMLUL4Lexer(DelegatingLexer):
    """
    Lexer for UL4 embedded in HTML.
    """

    name = "HTML+UL4"
    aliases = ["html+ul4"]
    filenames = ["*.htmlul4"]

    def __init__(self, **options):
        super().__init__(HtmlLexer, UL4Lexer, **options)


class XMLUL4Lexer(DelegatingLexer):
    """
    Lexer for UL4 embedded in XML.
    """

    name = "XML+UL4"
    aliases = ["xml+ul4"]
    filenames = ["*.xmlul4"]

    def __init__(self, **options):
        super().__init__(XmlLexer, UL4Lexer, **options)


class CSSUL4Lexer(DelegatingLexer):
    """
    Lexer for UL4 embedded in CSS.
    """

    name = "CSS+UL4"
    aliases = ["css+ul4"]
    filenames = ["*.cssul4"]

    def __init__(self, **options):
        super().__init__(CssLexer, UL4Lexer, **options)


class JavascriptUL4Lexer(DelegatingLexer):
    """
    Lexer for UL4 embedded in Javascript.
    """

    name = "Javascript+UL4"
    aliases = ["js+ul4"]
    filenames = ["*.jsul4"]

    def __init__(self, **options):
        super().__init__(JavascriptLexer, UL4Lexer, **options)


class PythonUL4Lexer(DelegatingLexer):
    """
    Lexer for UL4 embedded in Python.
    """

    name = "Python+UL4"
    aliases = ["py+ul4"]
    filenames = ["*.pyul4"]

    def __init__(self, **options):
        super().__init__(PythonLexer, UL4Lexer, **options)
