"""
This Calculator holds the logic for the calculator.
"""
from calculator.operators import Operator, STANDARD_OPERATORS
from calculator.expression import Term, Expression, TermExpression, OperatorExpression


class Calculator:
    """
    Calculator class is a simple calculator that can parse and evaluate expressions of the form:
    expression ::= term | expression operator expression
    operator ::= + | - | * | /
    with the usual precedence rules.
    """

    def __init__(self, operators=None):
        if operators is None:
            operators = STANDARD_OPERATORS
        self.operators = operators

    def tokenize(self, line: str):
        """
        Tokenize an expression into a list of tokens.
        """
        tokens = []
        for token in line.split():
            if token in self.operators:
                tokens.append(self.operators[token])
            else:
                try:
                    if int(token) == float(token):
                        tokens.append(int(token))
                except ValueError as exc:
                    raise ValueError(f"Invalid token {token}") from exc
        return tokens

    def parse(self, tokens) -> Expression:
        """
        Parse a list of tokens into an ordered expression.
        """
        if not tokens:
            raise ValueError("Empty expression")
        if len(tokens) == 1:
            if isinstance(tokens[0], Term):
                return TermExpression(tokens[0])
            raise ValueError(f"Expected a term, got {tokens[0]}")
        if len(tokens) == 2:
            raise ValueError("Invalid expression")

        # Find the rightest operator with the lowest precedence
        operator = None
        for i, token in enumerate(tokens):
            if isinstance(token, Operator):
                #####################################################################
                ### ATTENTION: Est-ce que c'est vraiment un < (non c'est un <=) ? ###
                #####################################################################
                if operator is None or token.precedence < operator.precedence:
                    operator = token
                    operator_index = i

        # Split the expression into two parts
        left = tokens[:operator_index]
        right = tokens[operator_index + 1:]

        # Parse the left and right parts recursively
        return OperatorExpression(operator, self.parse(left), self.parse(right))

    def __call__(self, expression: str) -> Term:
        return self.parse(self.tokenize(expression))()