import enum
from ASTTypes.ASTNode import ContextType
from ASTTypes.ASTNodeSubtypes import Expression, Token, TokenType
from typing import List, Self, Callable, Type
from logging import error, debug
from functools import wraps


class Operator(Expression):
    PRIORITY = 0

    def __init__(self, children: List[Expression]):
        super().__init__(children=children)


class TokenExpression(Expression):
    token_mapping: dict[TokenType, Type[Self]] = {}

    def __init__(self, token: Token,
                 children: list[Expression] | None = None):
        self.token = token
        self.value = token.value
        children: list[Expression] = children or []
        super().__init__(children)

    @classmethod
    def map_token(cls: Self, token_type: TokenType):
        def decorator(func: Callable):
            if token_type in cls.token_mapping:
                raise Exception("Can not map two handlers to a single token!")
            else:
                cls.token_mapping[token_type] = func
            return func
        return decorator

    @classmethod
    def from_token(cls, token: Token):
        return cls.token_mapping[token.token_type](token)


class SymbolType(enum.Enum):
    Scalar = "$"
    Array = "@"
    Hash = "%"


@TokenExpression.map_token(TokenType.Symbol)
class Symbol(TokenExpression):
    def __init__(self, token: Token):
        match [*token.value]:
            case ["$", *restname]:
                self.type = SymbolType.Scalar
                self.name = ''.join(restname)
                self.want_context = ContextType.SCALAR
            case ["@", *restname]:
                self.type = SymbolType.Array
                self.name = ''.join(restname)
                self.want_context = ContextType.LIST
            case ["%", *restname]:
                self.type = SymbolType.Hash
                self.name = ''.join(restname)
                self.want_context = ContextType.HASH
            case [*weirdname]:
                error(f"Found bad symbol '{''.join(weirdname)}'!")
                raise Exception(f"Can not parse symbol {''.join(weirdname)}")
        super().__init__(token)

    def __repr__(self):
        return f"{super().__repr__()} '{self.type.value}{self.name}'"

    def translate(self) -> str:
        match self.type:
            case SymbolType.Array:
                if self.context == ContextType.LIST:
                    return f"{self.name}"
                else:
                    return f"{self.name}.length"
            case _:
                return f"{self.name}"


@TokenExpression.map_token(TokenType.Number)
class Number(TokenExpression):
    def __repr__(self):
        return f"{super().__repr__()} '{self.token.value}'"

    def translate(self) -> str:
        return f"{self.token.value}"


@TokenExpression.map_token(TokenType.Structure)
class Structure(TokenExpression):
    def __repr__(self):
        return f"{super().__repr__()} '{self.token.value}¨'"


@TokenExpression.map_token(TokenType.Word)
class Word(TokenExpression):
    def __repr__(self):
        return f"{super().__repr__()} '{self.token.value}'"


@TokenExpression.map_token(TokenType.Quote)
class Quote(TokenExpression):
    def __init__(self, token: Token):
        super().__init__(token)
        valid_qq_delimiters=[
            ('(', ')'),
            ('[', ']'),
            ('{', '}'),
        ]
        match [*token.value]:
            case [delim1, *rest, delim2] if delim1 == delim2 and delim1 in ['\'', '\"']:
                self.value = ''.join(rest)
            case ["q", delim1, *rest, delim2] if delim1 == delim2 or (delim1, delim2) in valid_qq_delimiters:
                self.value = ''.join(rest)
            case ["q", "q", delim1, *rest, delim2] if delim1 == delim2 or (delim1, delim2) in valid_qq_delimiters:
                self.value = ''.join(rest)
            case ["q", " ", delim1, *rest, delim2] if delim1 == delim2:
                self.value = ''.join(rest)
            case ["q", "q", " ", delim1, *rest, delim2] if delim1 == delim2:
                self.value = ''.join(rest)
            case _:
                raise ValueError(f"Could not analyse quote token:'{token.value}'")

    def __repr__(self):
        base = super().__repr__()
        return f"{base} '{self.value}'"

    def translate(self) -> str:
        return f"\"{self.value}\""


class ParensStructure(Expression):
    def __init__(self, child: Expression | None = None):
        self._list = False
        self._varlist = False
        super().__init__([child])
        from ASTTypes.Operators import OperatorList
        if isinstance(child, OperatorList):
            self.children = child.children
            self._list = True
        if all(map(lambda x: isinstance(x, Symbol), self.children)):
            self._varlist = True
            self.want_context = ContextType.LIST

    @property
    def looks_like_parens(self) -> bool:
        if not self._list:
            return True
        return False

    @property
    def looks_like_varlist(self) -> bool:
        if self._varlist:
            return True
        return False

    def __repr__(self) -> str:
        base = super().__repr__()
        if self.looks_like_parens:
            return f"ParensStructure {base}"
        if self.looks_like_varlist:
            return f"VarListStructure {base}"
        return f"ListStructure {base}"

    def translate(self) -> str:
        if not self.looks_like_parens:
            children_translate = []
            for child in self.children:
                prefix = "..." if isinstance(child, Symbol) and child.type == SymbolType.Array else ""
                children_translate.append(f"{prefix}{child.translate()}")
            return f'[{", ".join(children_translate)}]'
        if self.looks_like_parens and not self.context == ContextType.LIST:
            return f"({self.children[0].translate()})"
        # Default behavior
        children_translate = []
        for child in self.children:
            prefix = "..." if isinstance(child, Symbol) and child.type == SymbolType.Array else ""
            children_translate.append(f"{prefix}{child.translate()}")
        return f'[{", ".join(children_translate)}]'