"""Degrees, Minutes, Seconds <=> Decimal Degrees."""

from __future__ import annotations

import re
from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:
    from numpy.typing import NDArray


def deg_to_dms(deg: float) -> NDArray[np.floating]:
    """Take in decimal degrees and output dms as a size 3 vector.

    Args:
        deg(float): Decimal degrees

    Returns:
        (deg,min,sec), all positive for North or East, all negative for South or West.
        Returned array will be floats, but the values for degrees and minutes will be
        whole numbers.
    """
    sign = 1 if deg >= 0 else -1
    deg *= sign
    ddd = int(deg // 1)
    deg -= ddd
    deg *= 60
    mm = int(deg // 1)
    deg -= mm
    deg *= 60
    ss = deg
    return np.array((ddd, mm, ss)) * sign


def deg_lat_to_dms_str(deg: float) -> str:
    """Take in decimal degrees and output dms as deg:mm'ss.s"N|S.

    Args:
        deg(float): Decimal degrees

    Returns:
        deg:mm'ss.s"N or deg:mm'ss.s"S
    """
    dms = deg_to_dms(deg)
    cardinal = "N"
    if any(i < 0 for i in dms):
        cardinal = "S"
        dms *= -1
    return f"{int(dms[0])}:{int(dms[1]):02d}'{dms[2]:04.1f}\"{cardinal}"


def deg_lon_to_dms_str(deg: float) -> str:
    """Take in decimal degrees and output dms as deg:mm'ss.s"E|W.

    Args:
        deg(float): Decimal degrees

    Returns:
        deg:mm'ss.s"E or deg:mm'ss.s"W
    """
    # sign = deg >= 0
    # cardinal = "E" if sign else "W"
    # if not sign:
    #     deg *= -1
    # ddd = int(deg // 1)
    # deg -= ddd
    # deg *= 60
    # mm = int(deg // 1)
    # deg -= mm
    # deg *= 60
    # ss = deg
    # return f"{ddd}:{mm:02d}'{ss:04.1f}\"{cardinal}"
    dms = deg_to_dms(deg)
    cardinal = "E"
    if any(i < 0 for i in dms):
        cardinal = "W"
        dms *= -1
    return f"{int(dms[0])}:{int(dms[1]):02d}'{dms[2]:04.1f}\"{cardinal}"


RE_DMS = re.compile(r"(\d+):(\d+)'(\d+\.?\d*)\"([NSEW])")


def dms_to_deg(dms: str) -> float:
    r"""Convert deg:mm'ss.s"N|S|E|W to decimal degrees.

    Regex used: "(\d+):(\d+)'(\d+\.?\d*)\"([NSEW])"

    Args:
        dms(str): A DMS string of the form deg:mm'ss.s"C

    Returns:
        Decimal degrees
    """
    matches = RE_DMS.match(dms)
    if not matches:
        raise ValueError("Not a valid DMS string")
    deg = float(matches[1])
    mm = int(matches[2])
    ss = float(matches[3])
    cardinal = matches[4]
    sign = 1 if cardinal in ["N", "E"] else -1
    deg += mm / 60
    deg += ss / 3600
    deg *= sign
    return deg
