from dataclasses import dataclass

import numpy as np

from .types import FunctionType, OperatorType, ValueType


def acot(x: ValueType):
    return np.arctan(1 / x)


def cot(x: ValueType):
    return 1 / np.tan(x)


functions: dict[str, FunctionType] = {
    "abs": np.abs,
    "acos": np.arccos,
    "acosh": np.arccosh,
    "acot": acot,
    "asin": np.arcsin,
    "asinh": np.arcsinh,
    "atan": np.arctan,
    "avg": np.average,
    "cos": np.cos,
    "cosh": np.cosh,
    "cot": cot,
    "exp": np.exp,
    "inf": np.inf,
    "lg": np.log10,
    "ln": np.log,
    "log10": np.log10,
    "log2": np.log2,
    "max": np.max,
    "min": np.min,
    "prod": np.prod,
    "sgn": np.sign,
    "sign": np.sign,
    "sin": np.sin,
    "sinh": np.sinh,
    "sqrt": np.sqrt,
    "sum": np.sum,
    "sup": np.max,
    "tah": np.tanh,
    "tan": np.tan,
}

operators: dict[str, OperatorType] = {
    "+": np.add,
    "-": np.subtract,
    "*": np.multiply,
    "/": np.divide,
    "%": np.mod,
    "^": np.float_power,
}

priorities: dict[str, int] = {
    "(": 0,
    "+": 1,
    "-": 1,
    "*": 2,
    "/": 2,
    "%": 2,
    "^": 3,
    "f": 4,  # function
    ")": 5,
}


@dataclass
class Operation:
    evaluator: (FunctionType | OperatorType | str)
    priority: int
    size: int


class FunctionOperation(Operation):
    def __init__(self, name: str):
        super().__init__(functions[name], priorities["f"], 1)


class BraceOperation(Operation):
    def __init__(self, name: str):
        super().__init__(name, priorities[name], 0)


class OperatorOperation(Operation):
    def __init__(self, name: str):
        super().__init__(operators[name], priorities[name], 2)
