import logging
from xml.etree import ElementTree as ElementTree
import XMLParse.ParserGlobals as Globals
from ASTTypes.ASTNodeSubtypes import Token, TokenType, Expression
from ASTTypes.ExpressionSubtypes import TokenExpression, ParensStructure
from XMLParse.ParserContainers import Parseable, ParserEnd, LiteralContainer, OPERATOR_CONTAINER_MAPPINGS, \
    OperatorFUNCTIONContainer


PRESUMED_LIST_OPERATORS = []
FORBIDDEN_WORD_OPERATORS = [
    "eval",  # There is no equivalent solution in target language
]


class Tokenizer:
    def __init__(self, tokens: list[ElementTree.Element]):
        self._tokens = tokens

    def next(self):
        self._tokens = self._tokens[1:]

    @property
    def token(self) -> Parseable:
        try:
            token = self._tokens[0]
        except IndexError:
            return ParserEnd()
        if token.tag == "Token":
            token = Token(value=token.text,
                          token_type=TokenType[token.attrib["Token-type"]],
                          line=int(token.attrib["line"]),
                          col=int(token.attrib["col"]))
            return self.analyze_token(token)
        elif token.tag == "Structure":
            return self.analyze_structure(token)

        raise SyntaxError(f"Tokenizer could not tokenize {token.tag} "
                          f"{token.attrib.get('Statement-type', '')}"
                          f"{token.attrib.get('Struct-type', '')}")

    def analyze_token(self, token: Token) -> Parseable:
        match token:
            case Token(token_type=TokenType.Word):
                return self.analyze_word_token(token)
            case Token(token_type=TokenType.Structure, value=";"):
                return ParserEnd()
            case Token(token_type=TokenType.Quote):
                return LiteralContainer(value=TokenExpression.from_token(token),
                                        line=token.line,
                                        column=token.col)
            case Token(token_type=TokenType.Number):
                return LiteralContainer(value=TokenExpression.from_token(token),
                                        line=token.line,
                                        column=token.col)
            case Token(token_type=TokenType.Symbol):
                return LiteralContainer(value=TokenExpression.from_token(token),
                                        line=token.line,
                                        column=token.col)
            case Token(token_type=TokenType.Operator):
                instantiate = OPERATOR_CONTAINER_MAPPINGS[token.value]
                return instantiate(line=token.line,
                                   column=token.col)
        raise SyntaxError(f"Could not tokenize token {token.token_type.name}:{token}")

    def analyze_word_token(self, token: Token) -> Parseable:
        from .ParserKnownWords import KNOWN_WORDS
        if token.value in FORBIDDEN_WORD_OPERATORS:
            raise SyntaxError(f"Found forbidden named operator '{token.value}'")
        if token.value in KNOWN_WORDS:
            instatntiate = KNOWN_WORDS[token.value]
            return instatntiate(line=token.line, column=token.col)
        if token.value not in PRESUMED_LIST_OPERATORS:
            logging.warning(f"Found unknown word '{token.value}', assumning its a list operator!")
            PRESUMED_LIST_OPERATORS.append(token.value)
        return OperatorFUNCTIONContainer(token.value, line=token.line, column=token.col)
        raise SyntaxError("Unknown word encountered")  # NOQA

    def analyze_structure(self, token: ElementTree.Element) -> Parseable:
        match token.attrib["Struct-type"]:
            case "List":
                sub_token = token[0]
                if not sub_token.tag == "Statement" and not sub_token.attrib["Statement-type"] == "Expression":
                    raise SyntaxError(f"Tokenizer got confused when parsing List Structure by unknown tag {sub_token.tag}")
                save = Globals.TOKENIZER.set(Tokenizer(list(sub_token)))
                sub_expression = Globals.PARSER(0)
                Globals.TOKENIZER.reset(save)
                return LiteralContainer(ParensStructure(sub_expression))
            case _:
                raise SyntaxError(f"Unknown Structure {token.attrib['Struct-type']}")


def parse_expression(right_binding_power: int = 0) -> Expression:
    tokenizer: Tokenizer = Globals.TOKENIZER.get()
    token = tokenizer.token
    tokenizer.next()
    left: Expression = token.null_denotation()
    while right_binding_power < tokenizer.token.left_binding_power:
        token = tokenizer.token
        tokenizer.next()
        left = token.left_denotation(left)
    return left


Globals.PARSER = parse_expression
