from typing import List, Optional, Tuple

from .coord_formatter import _Coord

__all__: List[str] = ["AngleCoord"]


class AngleCoord(_Coord):
    """
    Класс представляет собой угловую координату. Для координаты в формате числа с
    плавающей точкой (float) используется класс :class:`axipy.FloatCoord`.

    Угловую координату можно создать используя конструктор класса.

    .. literalinclude:: /../../tests/doc_examples/utl/test_example_angle_coord.py
        :caption: Создание угловой координаты конструктором класса.
        :pyobject: test_run_example_angle_coord
        :start-after: # start init
        :end-before: # finish init
        :dedent: 4

    Также, угловую координату можно создать из составляющих :meth:`from_parts`.
    """

    @classmethod
    def from_parts(cls, degrees: int, minutes: int = 0, seconds: float = 0.0) -> "AngleCoord":
        """
        Создает угловую координату из составляющих.

        Args:
            degrees: Градусы.
            minutes: Минуты.
            seconds: Секунды.
        """
        instance = cls.__new__(cls)
        instance._value = cls._reader_as_float(f"{degrees}°{minutes}'{seconds}\"")
        return instance

    @property
    def degrees(self) -> int:
        """Возвращает градусы."""
        d, _, _ = self._to_dms()
        return d

    @property
    def minutes(self) -> int:
        """Возвращает минуты."""
        _, m, _ = self._to_dms()
        return m

    @property
    def seconds(self) -> float:
        """Возвращает секунды."""
        _, _, s = self._to_dms()
        return s

    def as_string(
        self,
        delimiter: Optional[str] = None,
        precision: Optional[int] = None,
        suppress_trailing_zeros: bool = False,
    ) -> str:
        """
        Получение строкового значения угловой координаты.

        Args:
            delimiter:  Разделитель. Например, ``'-'`` или ``'/'``.
            precision: Количество знаков после запятой. Если None, округление не производится.
            suppress_trailing_zeros: Признак удаления завершающих нулей.
        """
        if precision is None:
            precision = -1

        if delimiter and isinstance(delimiter, str):
            return self._reader.as_string_with_delimeter(self._value, delimiter, precision, suppress_trailing_zeros)
        else:
            return self._reader.as_string(self._value, precision, suppress_trailing_zeros)

    def as_rumb(self, precision: Optional[int] = None, suppress_trailing_zeros: bool = False) -> str:
        """
        Получение строкового значения угловой координаты в формате румбов.

        Args:
            precision: Количество знаков после запятой. Если None, округление не производится.
            suppress_trailing_zeros: Признак удаления завершающих нулей.
        """
        if precision is None:
            precision = -1
        return self._reader.double_to_rumb(self._value, precision, suppress_trailing_zeros)

    def to_normalized(self, polar: bool = False) -> "AngleCoord":
        """
        Возвращает угловую координату, нормализованную в диапазоне [0; 360) или [-180;
        180].

        Args:
            polar: Если ``True``, то для нормализации используется полярная система координат [0; 360),
                если ``False``, то нормализация происходит в диапазоне [-180; 180].
        """
        value = self._value % 360
        if polar:
            if value < 0:
                value += 360
        else:
            if value > 180:
                value -= 360
            elif value < -180:
                value += 360

        return AngleCoord(value)

    def _to_dms(self) -> Tuple[int, int, float]:
        res = self._reader.split_degree(self._value)
        # round(x, 8), потому что на миллисекунды остается 8 знаков,
        # (15 - 3 знака в градусах - 2 знака в минутах - 2 знака в секундах до запятой).
        return res[0][0], res[0][1], round(res[1], 8)

    def _repr_arg(self) -> str:
        return f"'{self.as_string(delimiter='/')}'"
