# Copyright 2019-2021 by Felix Fontein <felix@fontein.de>
#
# Copyright 2006-2017 by the Pygments team, see AUTHORS at
# https://github.com/pygments/pygments/blob/3e1b79c8/AUTHORS
# Copyright by Norman Richards (original author of JSON lexer).
#
# Licensed under BSD license:
#
# Copyright (c) 2006-2017 by the respective authors (see AUTHORS file).
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Pygments lexers for ansible console output."""

# pylint: disable=consider-using-f-string

from pygments import token
from pygments.lexer import DelegatingLexer, RegexLexer, bygroups, include
from pygments.lexers import DiffLexer  # pylint: disable=no-name-in-module


class AnsibleOutputPrimaryLexer(RegexLexer):
    """Primary lexer for Ansible output."""

    name = 'Ansible-output-primary'

    # The following definitions are borrowed from Pygment's JSON lexer.
    # It has been originally authored by Norman Richards.

    # integer part of a number
    int_part = r'-?(0|[1-9]\d*)'

    # fractional part of a number
    frac_part = r'\.\d+'

    # exponential part of a number
    exp_part = r'[eE](\+|-)?\d+'

    tokens = {
        # #########################################
        # # BEGIN: states from JSON lexer #########
        # #########################################
        'whitespace': [
            (r'\s+', token.Text),
        ],

        # represents a simple terminal value
        'simplevalue': [
            (r'(true|false|null)\b', token.Keyword.Constant),
            (
                (
                    '%(int_part)s(%(frac_part)s%(exp_part)s|'
                    '%(exp_part)s|%(frac_part)s)'
                ) % vars(),
                token.Number.Float,
            ),
            (int_part, token.Number.Integer),
            (r'"(\\\\|\\"|[^"])*"', token.String),
        ],


        # the right hand side of an object, after the attribute name
        'objectattribute': [
            include('value'),
            (r':', token.Punctuation),
            # comma terminates the attribute but expects more
            (r',', token.Punctuation, '#pop'),
            # a closing bracket terminates the entire object, so pop twice
            (r'\}', token.Punctuation, '#pop:2'),
        ],

        # a json object - { attr, attr, ... }
        'objectvalue': [
            include('whitespace'),
            (r'"(\\\\|\\"|[^"])*"', token.Name.Tag, 'objectattribute'),
            (r'\}', token.Punctuation, '#pop'),
        ],

        # json array - [ value, value, ... ]
        'arrayvalue': [
            include('whitespace'),
            include('value'),
            (r',', token.Punctuation),
            (r'\]', token.Punctuation, '#pop'),
        ],

        # NOTE: This is a JSON value - either a simple value or a complex
        # NOTE: value (object or array):
        'value': [
            include('whitespace'),
            include('simplevalue'),
            (r'\{', token.Punctuation, 'objectvalue'),
            (r'\[', token.Punctuation, 'arrayvalue'),
        ],
        # #########################################
        # # END: states from JSON lexer ###########
        # #########################################

        'host-postfix': [
            (r'\n', token.Text, '#pop:3'),
            (
                r'( )(=>)( )(\{)',
                bygroups(
                    token.Text, token.Punctuation,
                    token.Text, token.Punctuation,
                ),
                'objectvalue',
            ),
        ],

        'host-error': [
            (
                r'(?:(:)( )(UNREACHABLE|FAILED)(!))?',
                bygroups(
                    token.Punctuation, token.Text,
                    token.Keyword, token.Punctuation,
                ),
                'host-postfix',
            ),
            (r'', token.Text, 'host-postfix'),
        ],

        'host-name': [
            (
                r'(\[)([^ \]]+)(?:( )(=>)( )([^\]]+))?(\])',
                bygroups(
                    token.Punctuation, token.Name.Variable, token.Text,
                    token.Punctuation, token.Text, token.Name.Variable,
                    token.Punctuation,
                ),
                'host-error',
            ),
        ],

        'host-result': [
            (r'\n', token.Text, '#pop'),
            (
                (
                    r'( +)(ok|changed|failed|skipped'
                    r'|unreachable|rescued|ignored)(=)([0-9]+)'
                ),
                bygroups(
                    token.Text, token.Keyword,
                    token.Punctuation, token.Number.Integer,
                ),
            ),
        ],

        'root': [
            (
                r'(PLAY|TASK|PLAY RECAP)(?:( )(\[)([^\]]+)(\]))?( )(\*+)(\n)',
                bygroups(
                    token.Keyword, token.Text, token.Punctuation,
                    token.Literal, token.Punctuation, token.Text,
                    token.Name.Variable, token.Text,
                ),
            ),
            (
                r'(fatal|ok|changed|skipping)(:)( )',
                bygroups(token.Keyword, token.Punctuation, token.Text),
                'host-name',
            ),
            (
                r'(\[)(WARNING)(\]:)([^\n]+)',
                bygroups(
                    token.Punctuation, token.Keyword,
                    token.Punctuation, token.Text,
                ),
            ),
            (
                r'([^ ]+)( +)(:)',
                bygroups(token.Name, token.Text, token.Punctuation),
                'host-result',
            ),
            (
                r'(\tto retry, use: )(.*)(\n)',
                bygroups(token.Text, token.Literal.String, token.Text),
            ),
            (r'.*\n', token.Other),
        ],
    }


class AnsibleOutputLexer(DelegatingLexer):
    """The Ansible output Pygments lexer."""

    name = 'Ansible-output'
    aliases = ('ansible-output',)

    def __init__(self, **options):
        """Initialize the lexer with delegation."""
        super().__init__(DiffLexer, AnsibleOutputPrimaryLexer, **options)
