from ASTTypes.ASTNodeSubtypes import Expression
from ASTTypes.ExpressionSubtypes import Operator
from ASTTypes.OperatorSubtypes import BinaryOperator
from ASTTypes.Operators import *
from typing import Type
import XMLParse.ParserGlobals as Globals


# Classes
class Parseable:
    left_binding_power = 0

    def __init__(self, line: int | None = None, column: int | None = None):
        self._line = line
        self._column = column

    def null_denotation(self) -> Expression:
        if self._line and self._column:
            raise SyntaxError(f"Unexpected token type {self.__class__.__name__} on line {self._line}, "
                              f"column {self._column}. Details: NUD")
        raise SyntaxError(f"Unexpected token type:{self.__class__.__name__}. Details: NUD")
        return Expression()  # NOQA Just to shut the typechecker up. This is supposed to be unreachable, its virtual

    def left_denotation(self, left: Expression) -> Expression:
        if self._line and self._column:
            raise SyntaxError(f"Unexpected token type {self.__class__.__name__} on line {self._line}, "
                              f"column {self._column}. Details: LED")
        raise SyntaxError(f"Unexpected token type:{self.__class__.__name__}. Details: LED")
        return Expression()  # NOQA Ditto

    def __repr__(self):
        return self.__class__.__name__


class ParserEnd(Parseable):
    left_binding_power = 0


class LiteralContainer(Parseable):
    def __init__(self, value: Expression,
                 line: int | None = None, column: int | None = None):
        super().__init__(line=line, column=column)
        self._value = value

    def null_denotation(self):
        return self._value


class BinaryLeftContainer(Parseable):
    OPERATOR_AST_CLASS: Type[BinaryOperator] | None = None

    def __init__(self,
                 line: int | None = None,
                 column: int | None = None):
        super().__init__(line=line, column=column)

    def left_denotation(self, left: Expression) -> Expression:
        right: Expression = Globals.PARSER(self.left_binding_power)
        return self.OPERATOR_AST_CLASS(leftchild=left, rightchild=right)


class BinaryLeftUnaryRightCombinedContainer(Parseable):
    BINARY_OPERATOR_AST_CLASS: Type[BinaryOperator] | None = None
    # BINARY_OPERATOR_PRIORITY is left_binding_power
    UNARY_OPERATOR_AST_CLASS: Type[UnaryOperator] | None = None
    UNARY_OPERATOR_PRIORITY: int = 0

    def left_denotation(self, left: Expression) -> Expression:
        right: Expression = Globals.PARSER(self.left_binding_power)
        return self.BINARY_OPERATOR_AST_CLASS(left, right)

    def null_denotation(self) -> Expression:
        right: Expression = Globals.PARSER(self.UNARY_OPERATOR_PRIORITY -1)
        return self.UNARY_OPERATOR_AST_CLASS(right)


class UnaryRightContainer(Parseable):
    OPERATOR_AST_CLASS: Type[UnaryOperator] | None = None
    UNARY_PRIORITY: int = 0

    def null_denotation(self) -> Expression:
        right: Expression = Globals.PARSER(self.UNARY_PRIORITY - 1)
        return self.OPERATOR_AST_CLASS(right)


class BinaryRightContainer(Parseable):
    OPERATOR_AST_CLASS: Type[BinaryOperator] | None = None
    left_binding_power = 0

    def left_denotation(self, left: Expression) -> Expression:
        right: Expression = Globals.PARSER(self.left_binding_power-1)
        return self.OPERATOR_AST_CLASS(leftchild=left, rightchild=right)


class UnaryPrefixPostfixContainer(Parseable):
    OPERATOR_AST_CLASS_POST: Type[UnaryOperator] | None = None
    OPERATOR_AST_CLASS_PRE: Type[UnaryOperator] | None = None
    left_binding_power = 0

    def null_denotation(self) -> Expression:
        right = Globals.PARSER(self.left_binding_power)
        return self.OPERATOR_AST_CLASS_PRE(right)

    def left_denotation(self, left: Expression) -> Expression:
        return self.OPERATOR_AST_CLASS_POST(left)


class OperatorFUNCTIONContainer(Parseable):
    left_binding_power = OperatorFUNCTION.PRIORITY

    def __init__(self, name, line: int | None = None, column: int | None = None):
        super().__init__(line, column)
        self.name = name

    def null_denotation(self) -> Expression:
        try:
            parens_possibly = Globals.TOKENIZER.get().token._value  # NOQA Yes, im cheating :|
            if isinstance(parens_possibly, ParensStructure):
                Globals.TOKENIZER.get().next()
                return OperatorFUNCTION(self.name, parens_possibly.children)
        except AttributeError:
            pass
        right = Globals.PARSER(self.left_binding_power)
        if isinstance(right, OperatorList):
            return OperatorFUNCTION(self.name, right.children)
        if isinstance(right, ParensStructure):
            return OperatorFUNCTION(self.name, right.children)
        return OperatorFUNCTION(self.name, [right])


# Factory functions
def add_prefix_postfix(token_text: str,
                       prefix_class: Type[UnaryOperator],
                       postfix_class: Type[UnaryOperator]):
    class MappedClass(UnaryPrefixPostfixContainer):
        OPERATOR_AST_CLASS_PRE = prefix_class
        OPERATOR_AST_CLASS_POST = postfix_class
        left_binding_power = prefix_class.PRIORITY
    MappedClass.__name__ = f"<Operator {token_text}>"
    OPERATOR_CONTAINER_MAPPINGS[token_text] = MappedClass


def add_unary_right(token_text: str,
                    unary_class: Type[UnaryOperator]):
    class MappedOperator(UnaryRightContainer):
        OPERATOR_AST_CLASS = unary_class
        UNARY_PRIORITY = unary_class.PRIORITY
    MappedOperator.__name__ = f"<Operator {token_text}>"
    OPERATOR_CONTAINER_MAPPINGS[token_text] = MappedOperator


def add_binary_right(token_text: str,
                     binary_class: Type[BinaryOperator]):
    class MappedOperator(BinaryRightContainer):
        OPERATOR_AST_CLASS = binary_class
        left_binding_power = binary_class.PRIORITY
    MappedOperator.__name__ = f"<Operator {token_text}>"
    OPERATOR_CONTAINER_MAPPINGS[token_text] = MappedOperator


def add_binary_left(token_text: str, operator_ast_class: Type[Operator]):
    class MappedOperator(BinaryLeftContainer):
        OPERATOR_AST_CLASS = operator_ast_class
        left_binding_power = operator_ast_class.PRIORITY
    MappedOperator.__name__ = f"<Operator {token_text}>"
    OPERATOR_CONTAINER_MAPPINGS[token_text] = MappedOperator


def add_binary_left_unary_right(token_text: str,
                                binary_class: Type[BinaryOperator],
                                unary_class: Type[UnaryOperator]):
    class MappedOperator(BinaryLeftUnaryRightCombinedContainer):
        BINARY_OPERATOR_AST_CLASS = binary_class
        left_binding_power = binary_class.PRIORITY
        UNARY_OPERATOR_AST_CLASS = unary_class
        UNARY_OPERATOR_PRIORITY = unary_class.PRIORITY
    MappedOperator.__name__ = f"<Operator {token_text}>"
    OPERATOR_CONTAINER_MAPPINGS[token_text] = MappedOperator


OPERATOR_CONTAINER_MAPPINGS: dict[str, Type[Parseable]] = {}


# HERE GO OPERATOR DEFINITIONS!
add_binary_left("*", OperatorMultiplication)
add_binary_left("/", OperatorDivision)
add_binary_left("%", OperatorModulo)
add_binary_left("x", OperatorRepetition)
add_binary_left(",", OperatorList)
add_binary_left("=>", OperatorList)  # AKA Fat comma
add_binary_left("<", OperatorLessThan)
add_binary_left(">", OperatorGreaterThan)
add_binary_left("<=", OperatorLessOrEqual)
add_binary_left(">=", OperatorGreaterOrEqual)
add_binary_left("lt", OperatorLessThan_str)
add_binary_left("gt", OperatorGreaterThan_str)
add_binary_left("le", OperatorLessOrEqual_str)
add_binary_left("ge", OperatorGreaterOrEqual_str)
add_binary_left(".", OperatorConcatenation)
add_binary_left("==", OperatorEquals)
add_binary_left("!=", OperatorNotEquals)
add_binary_left("eq", OperatorEquals_str)
add_binary_left("ne", OperatorNotEquals_str)

add_binary_left_unary_right("+",
                            binary_class=OperatorPlus_bin,
                            unary_class=OperatorPlus_un)
add_binary_left_unary_right("-",
                            binary_class=OperatorMinus_bin,
                            unary_class=OperatorMinus_un)

add_unary_right("!", unary_class=OperatorNot)

add_binary_right("=", binary_class=OperatorAssignment)

add_prefix_postfix("--", prefix_class=OperatorPredecrement, postfix_class=OperatorPostdecrement)
add_prefix_postfix("++", prefix_class=OperatorPreincrement, postfix_class=OperatorPostincrement)
