from parsimonious.grammar import Grammar
from parsimonious.nodes import NodeVisitor

from brake.model import Target, TargetDecorator

grammar = Grammar("""
    start         = target+
    target        = target_decorator name ":" NEWLINE command_block NEWLINE*
    target_decorator     = "@" ("task"/"file") target_args* NEWLINE
    target_args   = "(" kv* ")"
    kv            =  name "=" (list / string / bool) ","? " "? NEWLINE?
    command_block = command*
    command       = INDENT LINE NEWLINE

    NEWLINE = ~r"\\n"
    string = ~'"[^\"]*"'
    list   =  "[" list_member* "]"
    list_member = (" "* name " "* ","?)
    bool   = ("true"/"false")
    LINE   = ~"[^\\n]+"  # Match any line not containing a newline
    name   = ~r"[a-zA-Z*][a-zA-Z0-9_\\-./*]*"
    INDENT = ~r"\\s{4}"  # 4 spaces for indentation
    DEDENT = ~r"\\s{0,3}"  # Allow variable indentation sizes after a block

""")


class TargetVisitor(NodeVisitor):
    def visit_start(self, node, visited_children):
        return visited_children

    def visit_target(self, node, visited_children):
        decorator, target_name, *_, command_block, _ = visited_children
        kwargs = decorator.args.copy()
        if kwargs.get("default"):
            kwargs["default"] = True
        return Target(
            name=target_name,
            type=decorator.type,
            commands=command_block,
            **kwargs,
        )

    def visit_target_decorator(self, node, visited_children):
        _, target_type, task_args, _ = visited_children
        task_args = task_args[0] if (isinstance(task_args, list) and task_args) else {}
        return TargetDecorator(type=target_type[0].text, args=task_args)

    def visit_target_args(self, node, visited_children):
        args = {}
        openinig_bracket, kvs, closing_bracket = visited_children
        for kv in kvs:
            args.update(kv)
        return args

    def visit_kv(self, node, visited_children):
        kv = {}
        key, _, val, *_ = visited_children
        kv[key] = val[0]  # not sure why I have to index here
        return kv

    def visit_command_block(self, node, visited_children):
        return visited_children

    def visit_command(self, node, visited_children):
        indent, cmd, newline = node.children
        return cmd.text

    def visit_list(self, node, visited_children):
        opening_bracket, members, closing_bracket = visited_children
        if isinstance(members, list):
            return members
        return members.children

    def visit_string(self, node, visited_children):
        return node.text.strip('"')

    def visit_bool(self, node, visited_children):
        return eval(node.text.capitalize())

    def visit_list_member(self, node, visited_children):
        _, member, *_ = visited_children
        return member

    def visit_name(self, node, visited_children):
        return node.text

    def generic_visit(self, node, visited_children):
        # For any node we don't explicitly handle
        return visited_children or node
