from typing import List

from .data_types import AngleType
from .utils import normalize_angle, save_precision


class CartesianAngleConverter:
    """
    Преобразует углы переданные в разных форматах к единому
    формату значений используемому в Аксиоме для решения
    геодезической задачи (0 справа, значения растут против часовой стрелки)
    """

    def __init__(self):
        self.__view_angles = None

    @staticmethod
    def __nord_to_axi_angle_non_earth(angle: float) -> float:
        return normalize_angle(-angle + 90)

    @staticmethod
    def __axi_angle_to_nord_non_earth(angle: float) -> float:
        return normalize_angle(-angle + 90)

    def set_view_angles(self, angles: List[float]):
        self.__view_angles = angles

    def __from_axi_angle_impl_plan_schema(
        self, index: int, angle: float, angle_type: AngleType, prev_value: float
    ) -> float:
        if angle_type == AngleType.ClockWiseTop:
            return self.__axi_angle_to_nord_non_earth(angle)
        elif angle_type == AngleType.CounterClockWiseRight:
            return angle
        elif angle_type == AngleType.Relate:
            fixed_angle = self.__axi_angle_to_nord_non_earth(angle)
            fixed_prev_value = self.__axi_angle_to_nord_non_earth(prev_value)
            if index == 1:
                # Угол для первого индекс никакого смысла не имеет. Сдедовательно
                # не следует его учитывать при вычислении относительного угла для
                # второй точки. Этот угол всегда является углом на север.
                return fixed_angle
            return fixed_angle - fixed_prev_value
        elif angle_type == AngleType.Math:
            return angle

    def from_axi_angle(
        self,
        *,
        index: int,
        angle: float,
        prev_angle: float,
        angle_type: AngleType,
    ) -> float:
        """
        TODO: Добавить описание
        """

        def evaluate():
            return self.__from_axi_angle_impl_plan_schema(
                index=index, angle=angle, angle_type=angle_type, prev_value=prev_angle
            )

        return save_precision(evaluate, angle)

    def __to_axi_angle_non_earh(self, angle: float, angle_type: AngleType, current_index: int) -> float:
        if angle_type == AngleType.ClockWiseTop:
            return self.__nord_to_axi_angle_non_earth(angle)
        elif angle_type == AngleType.CounterClockWiseRight:
            return angle
        elif angle_type == AngleType.Relate:
            axi_angle = sum(self.__view_angles[: current_index + 1])
            return self.__nord_to_axi_angle_non_earth(axi_angle)
        elif angle_type == AngleType.Math:
            return -angle + 90
        return angle

    def to_axi_angle(self, angle: float, angle_type: AngleType, current_index: int) -> float:
        """
        Преобразует угол введённый пользователем во внутреннее представление
        """

        # Угол на север имеет 0 вверху и растёт по часовой стрелке.
        #
        # Угол на восток имеет 0 справа и растёт против часовой стрелки.
        #
        # Угол относительно текущего полжения всегда заворачивает направо относительно
        # последнего отрезка. Т.е. имеет 0 по направлению последнего отрезка и
        # растёт по часовой стрелке.
        #
        # Однако положение дел может меняться в зависимости от проекций, от того в какую
        # сторону там направление на север/восток. Вышепреведённые
        # соображения справедливы для долготы/широты, план-схемы, проекций мира.
        def evaluate():
            normalized_angle = normalize_angle(angle)
            axi_angle = self.__to_axi_angle_non_earh(normalized_angle, angle_type, current_index)
            axi_angle = normalize_angle(axi_angle)
            return axi_angle

        return save_precision(evaluate, angle)
