"""
    Based on custom lexers for GitHub.com

    :copyright: Copyright 2012 by GitHub, Inc
    :license: BSD, see LICENSE for details.
"""

from pygments.lexer import (
    DelegatingLexer,
    ExtendedRegexLexer,
    RegexLexer,
    bygroups,
    include,
)
from pygments.token import (
    Comment,
    Keyword,
    Literal,
    Name,
    Number,
    Operator,
    Other,
    Punctuation,
    String,
    Text,
    Whitespace,
)

__all__ = ['Dasm16Lexer', 'PuppetLexer', 'AugeasLexer', 'SlashLexer']


class Dasm16Lexer(RegexLexer):

    """
    Simple lexer for DCPU-16 Assembly

    Check http://0x10c.com/doc/dcpu-16.txt
    """

    name = 'dasm16'
    aliases = ['DASM16']
    filenames = ['*.dasm16', '*.dasm']
    mimetypes = ['text/x-dasm16']

    INSTRUCTIONS = [
        'SET',
        'ADD',
        'SUB',
        'MUL',
        'MLI',
        'DIV',
        'DVI',
        'MOD',
        'MDI',
        'AND',
        'BOR',
        'XOR',
        'SHR',
        'ASR',
        'SHL',
        'IFB',
        'IFC',
        'IFE',
        'IFN',
        'IFG',
        'IFA',
        'IFL',
        'IFU',
        'ADX',
        'SBX',
        'STI',
        'STD',
        'JSR',
        'INT',
        'IAG',
        'IAS',
        'RFI',
        'IAQ',
        'HWN',
        'HWQ',
        'HWI',
    ]

    REGISTERS = [
        'A',
        'B',
        'C',
        'X',
        'Y',
        'Z',
        'I',
        'J',
        'SP',
        'PC',
        'EX',
        'POP',
        'PEEK',
        'PUSH',
    ]

    # Regexes yo
    char = r'[a-zA-Z$._0-9@]'
    identifier = r'(?:[a-zA-Z$_]' + char + r'*|\.' + char + '+)'
    number = r'[+-]?(?:0[xX][a-zA-Z0-9]+|\d+)'
    binary_number = r'0b[01_]+'
    instruction = r'(?i)(' + '|'.join(INSTRUCTIONS) + ')'
    single_char = r"'\\?" + char + "'"
    string = r'"(\\"|[^"])*"'

    def guess_identifier(lexer, match):
        ident = match.group(0)
        klass = Name.Variable if ident.upper() in lexer.REGISTERS else Name.Label
        yield match.start(), klass, ident

    tokens = {
        'root': [
            include('whitespace'),
            (':' + identifier, Name.Label),
            (identifier + ':', Name.Label),
            (instruction, Name.Function, 'instruction-args'),
            (r'\.' + identifier, Name.Function, 'data-args'),
            (r'[\r\n]+', Text),
        ],
        'numeric': [
            (binary_number, Number.Integer),
            (number, Number.Integer),
            (single_char, String),
        ],
        'arg': [(identifier, guess_identifier), include('numeric')],
        'deref': [
            (r'\+', Punctuation),
            (r'\]', Punctuation, '#pop'),
            include('arg'),
            include('whitespace'),
        ],
        'instruction-line': [
            (r'[\r\n]+', Text, '#pop'),
            (r';.*?$', Comment, '#pop'),
            include('whitespace'),
        ],
        'instruction-args': [
            (r',', Punctuation),
            (r'\[', Punctuation, 'deref'),
            include('arg'),
            include('instruction-line'),
        ],
        'data-args': [
            (r',', Punctuation),
            include('numeric'),
            (string, String),
            include('instruction-line'),
        ],
        'whitespace': [(r'\n', Text), (r'\s+', Text), (r';.*?\n', Comment)],
    }


class PuppetLexer(RegexLexer):
    name = 'Puppet'
    aliases = ['puppet']
    filenames = ['*.pp']

    tokens = {
        'root': [include('puppet')],
        'puppet': [
            include('comments'),
            (
                r'(class)(\s*)(\{)',
                bygroups(Name.Class, Text, Punctuation),
                ('type', 'namevar'),
            ),
            (r'(class|define)', Keyword.Declaration, ('block', 'class_name')),
            (r'node', Keyword.Declaration, ('block', 'node_name')),
            (r'elsif', Keyword.Reserved, ('block', 'conditional')),
            (r'if', Keyword.Reserved, ('block', 'conditional')),
            (r'unless', Keyword.Reserved, ('block', 'conditional')),
            (
                r'(else)(\s*)(\{)',
                bygroups(Keyword.Reserved, Text, Punctuation),
                'block',
            ),
            (r'case', Keyword.Reserved, ('case', 'conditional')),
            (
                r'(::)?([A-Z][\w:]+)+(\s*)(<{1,2}\|)',
                bygroups(Name.Class, Name.Class, Text, Punctuation),
                'spaceinvader',
            ),
            (
                r'(::)?([A-Z][\w:]+)+(\s*)(\{)',
                bygroups(Name.Class, Name.Class, Text, Punctuation),
                'type',
            ),
            (
                r'(::)?([A-Z][\w:]+)+(\s*)(\[)',
                bygroups(Name.Class, Name.Class, Text, Punctuation),
                ('type', 'override_name'),
            ),
            (
                r'(@{0,2}[\w:]+)(\s*)(\{)(\s*)',
                bygroups(Name.Class, Text, Punctuation, Text),
                ('type', 'namevar'),
            ),
            (r'\$(::)?(\w+::)*\w+', Name.Variable, 'var_assign'),
            (r'(include|require)', Keyword.Namespace, 'include'),
            (r'import', Keyword.Namespace, 'import'),
            (r'(\w+)(\()', bygroups(Name.Function, Punctuation), 'function'),
            (r'\s', Text),
        ],
        'block': [include('puppet'), (r'\}', Text, '#pop')],
        'override_name': [
            include('strings'),
            include('variables'),
            (r'\]', Punctuation),
            (r'\s', Text),
            (r'\{', Punctuation, '#pop'),
        ],
        'node_name': [
            (r'inherits', Keyword.Declaration),
            (r'[\w\.]+', String),
            include('strings'),
            include('variables'),
            (r',', Punctuation),
            (r'\s', Text),
            (r'\{', Punctuation, '#pop'),
        ],
        'class_name': [
            (r'inherits', Keyword.Declaration),
            (r'[\w:]+', Name.Class),
            (r'\s', Text),
            (r'\{', Punctuation, '#pop'),
            (r'\(', Punctuation, 'paramlist'),
        ],
        'include': [
            (r'\n', Text, '#pop'),
            (r'[\w:-]+', Name.Class),
            include('value'),
            (r'\s', Text),
        ],
        'import': [
            (r'\n', Text, '#pop'),
            (r'[\/\w\.]+', String),
            include('value'),
            (r'\s', Text),
        ],
        'case': [
            (
                r'(default)(:)(\s*)(\{)',
                bygroups(Keyword.Reserved, Punctuation, Text, Punctuation),
                'block',
            ),
            include('case_values'),
            (r'(:)(\s*)(\{)', bygroups(Punctuation, Text, Punctuation), 'block'),
            (r'\s', Text),
            (r'\}', Punctuation, '#pop'),
        ],
        'case_values': [include('value'), (r',', Punctuation)],
        'comments': [(r'\s*#.*\n', Comment.Singleline)],
        'strings': [
            (r"'.*?'", String.Single),
            (r'\w+', String.Symbol),
            (r'"', String.Double, 'dblstring'),
            (r'\/.+?\/', String.Regex),
        ],
        'dblstring': [
            (r'\$\{.+?\}', String.Interpol),
            (
                r'(?:\\(?:[bdefnrstv\'"\$\\/]|[0-7][0-7]?[0-7]?|\^[a-zA-Z]))',
                String.Escape,
            ),
            (r'[^"\\\$]+', String.Double),
            (r'\$', String.Double),
            (r'"', String.Double, '#pop'),
        ],
        'variables': [(r'\$(::)?(\w+::)*\w+', Name.Variable)],
        'var_assign': [
            (r'\[', Punctuation, ('#pop', 'array')),
            (r'\{', Punctuation, ('#pop', 'hash')),
            (r'(\s*)(=)(\s*)', bygroups(Text, Operator, Text)),
            (r'(\(|\))', Punctuation),
            include('operators'),
            include('value'),
            (r'\s', Text, '#pop'),
        ],
        'booleans': [(r'(true|false)', Literal)],
        'operators': [
            (
                r'(\s*)(==|=~|\*|-|\+|<<|>>|!=|!~|!|>=|<=|<|>|and|or|in)(\s*)',
                bygroups(Text, Operator, Text),
            )
        ],
        'conditional': [
            include('operators'),
            include('strings'),
            include('variables'),
            (r'\[', Punctuation, 'array'),
            (r'\(', Punctuation, 'conditional'),
            (r'\{', Punctuation, '#pop'),
            (r'\)', Punctuation, '#pop'),
            (r'\s', Text),
        ],
        'spaceinvader': [
            include('operators'),
            include('strings'),
            include('variables'),
            (r'\[', Punctuation, 'array'),
            (r'\(', Punctuation, 'conditional'),
            (r'\s', Text),
            (r'\|>{1,2}', Punctuation, '#pop'),
        ],
        'namevar': [
            include('value'),
            (r'\[', Punctuation, 'array'),
            (r'\s', Text),
            (r':', Punctuation, '#pop'),
            (r'\}', Punctuation, '#pop'),
        ],
        'function': [
            (r'\[', Punctuation, 'array'),
            include('value'),
            (r',', Punctuation),
            (r'\s', Text),
            (r'\)', Punctuation, '#pop'),
        ],
        'paramlist': [
            include('value'),
            (r'=', Punctuation),
            (r',', Punctuation),
            (r'\s', Text),
            (r'\[', Punctuation, 'array'),
            (r'\)', Punctuation, '#pop'),
        ],
        'type': [
            (
                r'(\w+)(\s*)(=>)(\s*)',
                bygroups(Name.Tag, Text, Punctuation, Text),
                'param_value',
            ),
            (r'\}', Punctuation, '#pop'),
            (r'\s', Text),
            include('comments'),
            (r'', Text, 'namevar'),
        ],
        'value': [
            (r'[\d\.]', Number),
            (r'([A-Z][\w:]+)+(\[)', bygroups(Name.Class, Punctuation), 'array'),
            (r'(\w+)(\()', bygroups(Name.Function, Punctuation), 'function'),
            include('strings'),
            include('variables'),
            include('comments'),
            include('booleans'),
            (
                r'(\s*)(\?)(\s*)(\{)',
                bygroups(Text, Punctuation, Text, Punctuation),
                'selector',
            ),
            (r'\{', Punctuation, 'hash'),
        ],
        'selector': [
            (r'default', Keyword.Reserved),
            include('value'),
            (r'=>', Punctuation),
            (r',', Punctuation),
            (r'\s', Text),
            (r'\}', Punctuation, '#pop'),
        ],
        'param_value': [
            include('value'),
            (r'\[', Punctuation, 'array'),
            (r',', Punctuation, '#pop'),
            (r';', Punctuation, '#pop'),
            (r'\s', Text, '#pop'),
            (r'', Text, '#pop'),
        ],
        'array': [
            include('value'),
            (r'\[', Punctuation, 'array'),
            (r',', Punctuation),
            (r'\s', Text),
            (r'\]', Punctuation, '#pop'),
        ],
        'hash': [
            include('value'),
            (r'\s', Text),
            (r'=>', Punctuation),
            (r',', Punctuation),
            (r'\}', Punctuation, '#pop'),
        ],
    }


class AugeasLexer(RegexLexer):
    name = 'Augeas'
    aliases = ['augeas']
    filenames = ['*.aug']

    tokens = {
        'root': [
            (
                r'(module)(\s*)([^\s=]+)',
                bygroups(Keyword.Namespace, Text, Name.Namespace),
            ),
            (
                r'(let)(\s*)([^\s=]+)',
                bygroups(Keyword.Declaration, Text, Name.Variable),
            ),
            (
                r'(del|store|value|counter|seq|key|label|autoload|incl|'
                r'excl|transform|test|get|put)(\s+)',
                bygroups(Name.Builtin, Text),
            ),
            (
                r'(\()([^\:]+)(\:)(unit|string|regexp|lens|tree|filter)(\))',
                bygroups(
                    Punctuation, Name.Variable, Punctuation, Keyword.Type, Punctuation
                ),
            ),
            (r'\(\*', Comment.Multiline, 'comment'),
            (r'[\+=\|\.\*\;\?-]', Operator),
            (r'[\[\]\(\)\{\}]', Operator),
            (r'"', String.Double, 'string'),
            (r'\/', String.Regex, 'regex'),
            (
                r'([A-Z]\w*)(\.)(\w+)',
                bygroups(Name.Namespace, Punctuation, Name.Variable),
            ),
            (r'.', Name.Variable),
            (r'\s', Text),
        ],
        'string': [
            (r'\\.', String.Escape),
            (r'[^"]', String.Double),
            (r'"', String.Double, '#pop'),
        ],
        'regex': [
            (r'\\.', String.Escape),
            (r'[^\/]', String.Regex),
            (r'\/', String.Regex, '#pop'),
        ],
        'comment': [
            (r'[^*\)]', Comment.Multiline),
            (r'\(\*', Comment.Multiline, '#push'),
            (r'\*\)', Comment.Multiline, '#pop'),
            (r'[\*\)]', Comment.Multiline),
        ],
    }


class SlashLanguageLexer(ExtendedRegexLexer):
    _nkw = r'(?=[^a-zA-Z_0-9])'

    def move_state(new_state):
        return ('#pop', new_state)

    def right_angle_bracket(lexer, match, ctx):
        if len(ctx.stack) > 1 and ctx.stack[-2] == 'string':
            ctx.stack.pop()
        yield match.start(), String.Interpol, '}'
        ctx.pos = match.end()

    tokens = {
        'root': [
            (r'<%=', Comment.Preproc, move_state('slash')),
            (r'<%!!', Comment.Preproc, move_state('slash')),
            (r'<%#.*?%>', Comment.Multiline),
            (r'<%', Comment.Preproc, move_state('slash')),
            (r'.|\n', Other),
        ],
        'string': [
            (r'\\', String.Escape, move_state('string_e')),
            (r'\'', String, move_state('slash')),
            (r'#\{', String.Interpol, 'slash'),
            (r'.|\n', String),
        ],
        'string_e': [
            (r'n', String.Escape, move_state('string')),
            (r't', String.Escape, move_state('string')),
            (r'r', String.Escape, move_state('string')),
            (r'e', String.Escape, move_state('string')),
            (r'x[a-fA-F0-9]{2}', String.Escape, move_state('string')),
            (r'.', String.Escape, move_state('string')),
        ],
        'regexp': [
            (r'}[a-z]*', String.Regex, move_state('slash')),
            (r'\\(.|\n)', String.Regex),
            (r'{', String.Regex, 'regexp_r'),
            (r'.|\n', String.Regex),
        ],
        'regexp_r': [
            (r'}[a-z]*', String.Regex, '#pop'),
            (r'\\(.|\n)', String.Regex),
            (r'{', String.Regex, 'regexp_r'),
        ],
        'slash': [
            (r'%>', Comment.Preproc, move_state('root')),
            (r'\'', String, move_state('string')),
            (r"'[a-zA-Z0-9_]+", String),
            (r'%r{', String.Regex, move_state('regexp')),
            (r'/\*.*?\*/', Comment.Multiline),
            (r'(#|//).*?\n', Comment.Single),
            (r'-?[0-9]+e[+-]?[0-9]+', Number.Float),
            (r'-?[0-9]+\.[0-9]+(e[+-]?[0-9]+)?', Number.Float),
            (r'-?[0-9]+', Number.Integer),
            (r'nil' + _nkw, Name.Builtin),
            (r'true' + _nkw, Name.Builtin),
            (r'false' + _nkw, Name.Builtin),
            (r'self' + _nkw, Name.Builtin),
            (
                r'(class)(\s+)([A-Z][a-zA-Z0-9_\']*)',
                bygroups(Keyword, Whitespace, Name.Class),
            ),
            (r'class' + _nkw, Keyword),
            (r'extends' + _nkw, Keyword),
            (
                r'(def)(\s+)(self)(\s*)(\.)(\s*)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|'
                r'==|<=>|<=|<|>=|>|\+|-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)',
                bygroups(
                    Keyword,
                    Whitespace,
                    Name.Builtin,
                    Whitespace,
                    Punctuation,
                    Whitespace,
                    Name.Function,
                ),
            ),
            (
                r'(def)(\s+)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|==|<=>|<=|<|>=|>|\+|'
                r'-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)',
                bygroups(Keyword, Whitespace, Name.Function),
            ),
            (r'def' + _nkw, Keyword),
            (r'if' + _nkw, Keyword),
            (r'elsif' + _nkw, Keyword),
            (r'else' + _nkw, Keyword),
            (r'unless' + _nkw, Keyword),
            (r'for' + _nkw, Keyword),
            (r'in' + _nkw, Keyword),
            (r'while' + _nkw, Keyword),
            (r'until' + _nkw, Keyword),
            (r'and' + _nkw, Keyword),
            (r'or' + _nkw, Keyword),
            (r'not' + _nkw, Keyword),
            (r'lambda' + _nkw, Keyword),
            (r'try' + _nkw, Keyword),
            (r'catch' + _nkw, Keyword),
            (r'return' + _nkw, Keyword),
            (r'next' + _nkw, Keyword),
            (r'last' + _nkw, Keyword),
            (r'throw' + _nkw, Keyword),
            (r'use' + _nkw, Keyword),
            (r'switch' + _nkw, Keyword),
            (r'\\', Keyword),
            (r'λ', Keyword),
            (r'__FILE__' + _nkw, Name.Builtin.Pseudo),
            (r'__LINE__' + _nkw, Name.Builtin.Pseudo),
            (r'[A-Z][a-zA-Z0-9_\']*' + _nkw, Name.Constant),
            (r'[a-z_][a-zA-Z0-9_\']*' + _nkw, Name),
            (r'@[a-z_][a-zA-Z0-9_\']*' + _nkw, Name.Variable.Instance),
            (r'@@[a-z_][a-zA-Z0-9_\']*' + _nkw, Name.Variable.Class),
            (r'\(', Punctuation),
            (r'\)', Punctuation),
            (r'\[', Punctuation),
            (r'\]', Punctuation),
            (r'\{', Punctuation),
            (r'\}', right_angle_bracket),
            (r';', Punctuation),
            (r',', Punctuation),
            (r'<<=', Operator),
            (r'>>=', Operator),
            (r'<<', Operator),
            (r'>>', Operator),
            (r'==', Operator),
            (r'!=', Operator),
            (r'=>', Operator),
            (r'=', Operator),
            (r'<=>', Operator),
            (r'<=', Operator),
            (r'>=', Operator),
            (r'<', Operator),
            (r'>', Operator),
            (r'\+\+', Operator),
            (r'\+=', Operator),
            (r'-=', Operator),
            (r'\*\*=', Operator),
            (r'\*=', Operator),
            (r'\*\*', Operator),
            (r'\*', Operator),
            (r'/=', Operator),
            (r'\+', Operator),
            (r'-', Operator),
            (r'/', Operator),
            (r'%=', Operator),
            (r'%', Operator),
            (r'^=', Operator),
            (r'&&=', Operator),
            (r'&=', Operator),
            (r'&&', Operator),
            (r'&', Operator),
            (r'\|\|=', Operator),
            (r'\|=', Operator),
            (r'\|\|', Operator),
            (r'\|', Operator),
            (r'!', Operator),
            (r'\.\.\.', Operator),
            (r'\.\.', Operator),
            (r'\.', Operator),
            (r'::', Operator),
            (r':', Operator),
            (r'(\s|\n)+', Whitespace),
            (r'[a-z_][a-zA-Z0-9_\']*', Name.Variable),
        ],
    }


class SlashLexer(DelegatingLexer):

    """
    Lexer for the Slash programming language.
    """

    name = 'Slash'
    aliases = ['slash']
    filenames = ['*.sl']

    def __init__(self, **options):
        from pygments.lexers.web import HtmlLexer

        super().__init__(HtmlLexer, SlashLanguageLexer, **options)
