"""
Contains functions for translating report data
"""

from avwx.static.core import CLOUD_TRANSLATIONS
from avwx.structs import Cloud, Code, Number, SharedData, Units


def get_cardinal_direction(direction: int) -> str:
    """
    Returns the cardinal direction (NSEW) for a degree direction

    Wind Direction - Cheat Sheet:

    (360) -- 011/012 -- 033/034 -- (045) -- 056/057 -- 078/079 -- (090)

    (090) -- 101/102 -- 123/124 -- (135) -- 146/147 -- 168/169 -- (180)

    (180) -- 191/192 -- 213/214 -- (225) -- 236/237 -- 258/259 -- (270)

    (270) -- 281/282 -- 303/304 -- (315) -- 326/327 -- 348/349 -- (360)
    """
    ret = ""
    if not isinstance(direction, int):
        direction = int(direction)
    # Convert to range [0 360]
    while direction < 0:
        direction += 360
    direction = direction % 360
    if 304 <= direction <= 360 or 0 <= direction <= 56:
        ret += "N"
        if 304 <= direction <= 348:
            if 327 <= direction <= 348:
                ret += "N"
            ret += "W"
        elif 12 <= direction <= 56:
            if 12 <= direction <= 33:
                ret += "N"
            ret += "E"
    elif 124 <= direction <= 236:
        ret += "S"
        if 124 <= direction <= 168:
            if 147 <= direction <= 168:
                ret += "S"
            ret += "E"
        elif 192 <= direction <= 236:
            if 192 <= direction <= 213:
                ret += "S"
            ret += "W"
    elif 57 <= direction <= 123:
        ret += "E"
        if 57 <= direction <= 78:
            ret += "NE"
        elif 102 <= direction <= 123:
            ret += "SE"
    elif 237 <= direction <= 303:
        ret += "W"
        if 237 <= direction <= 258:
            ret += "SW"
        elif 282 <= direction <= 303:
            ret += "NW"
    return ret


WIND_DIR_REPR = {"000": "Calm", "VRB": "Variable"}


def wind(
    direction: Number,
    speed: Number,
    gust: Number,
    vardir: [Number] = None,
    unit: str = "kt",
    cardinals: bool = True,
    spoken: bool = False,
) -> str:
    """
    Format wind elements into a readable sentence

    Returns the translation string

    Ex: NNE-020 (variable 010 to 040) at 14kt gusting to 20kt
    """
    ret = ""
    target = "spoken" if spoken else "repr"
    # Wind direction
    if direction:
        if direction.repr in WIND_DIR_REPR:
            ret += WIND_DIR_REPR[direction.repr]
        elif direction.value is None:
            ret += direction.repr
        else:
            if cardinals:
                ret += get_cardinal_direction(direction.value) + "-"
            ret += getattr(direction, target)
    # Variable direction
    if vardir and isinstance(vardir, list):
        vardir = [getattr(var, target) for var in vardir]
        ret += " (variable {} to {})".format(*vardir)
    # Speed
    if speed and speed.value:
        ret += f" at {speed.value}{unit}"
    # Gust
    if gust and gust.value:
        ret += f" gusting to {gust.value}{unit}"
    return ret


VIS_REPR = {
    "P6": "Greater than 6sm ( >10km )",
    "M1/4": "Less than .25sm ( <0.4km )",
    "M1/8": "Less than .125sm ( <0.2km )",
}


def visibility(vis: Number, unit: str = "m") -> str:
    """
    Formats a visibility element into a string with both km and sm values

    Ex: 8km ( 5sm )
    """
    if not (vis and unit in ("m", "sm")):
        return ""
    if vis.repr in VIS_REPR:
        return VIS_REPR[vis.repr]
    if unit == "m":
        converted = vis.value * 0.000621371
        converted = str(round(converted, 1)).replace(".0", "") + "sm"
        value = str(round(vis.value / 1000, 1)).replace(".0", "")
        unit = "km"
    elif unit == "sm":
        converted = vis.value / 0.621371
        converted = str(round(converted, 1)).replace(".0", "") + "km"
        value = str(vis.value).replace(".0", "")
    return f"{value}{unit} ({converted})"


def temperature(temp: Number, unit: str = "C") -> str:
    """
    Formats a temperature element into a string with both C and F values

    Used for both Temp and Dew

    Ex: 34°C (93°F)
    """
    unit = unit.upper()
    if not (temp and unit in ("C", "F")):
        return ""
    if unit == "C":
        converted = temp.value * 1.8 + 32
        converted = str(int(round(converted))) + "°F"
    elif unit == "F":
        converted = (temp.value - 32) / 1.8
        converted = str(int(round(converted))) + "°C"
    return f"{temp.value}°{unit} ({converted})"


def altimeter(alt: Number, unit: str = "hPa") -> str:
    """
    Formats the altimeter element into a string with hPa and inHg values

    Ex: 30.11 inHg (10.20 hPa)
    """
    if not (alt and unit in ("hPa", "inHg")):
        return ""
    if unit == "hPa":
        value = alt.repr
        converted = alt.value / 33.8638866667
        converted = str(round(converted, 2)) + " inHg"
    elif unit == "inHg":
        value = alt.repr[:2] + "." + alt.repr[2:]
        converted = alt.value * 33.8638866667
        converted = str(int(round(converted))) + " hPa"
    return f"{value} {unit} ({converted})"


def clouds(values: [Cloud], unit: str = "ft") -> str:
    """
    Format cloud list into a readable sentence

    Returns the translation string

    Ex: Broken layer at 2200ft (Cumulonimbus), Overcast layer at 3600ft - Reported AGL
    """
    if values is None:
        return ""
    ret = []
    for cloud in values:
        if cloud.base is None:
            continue
        cloud_str = CLOUD_TRANSLATIONS[cloud.type]
        if cloud.modifier and cloud.modifier in CLOUD_TRANSLATIONS:
            cloud_str += f" ({CLOUD_TRANSLATIONS[cloud.modifier]})"
        ret.append(cloud_str.format(cloud.base * 100, unit))
    if ret:
        return ", ".join(ret) + " - Reported AGL"
    return "Sky clear"


def wx_codes(codes: [Code]) -> str:
    """
    Join WX code values

    Returns the translation string
    """
    return ", ".join(code.value for code in codes)


def current_shared(wxdata: SharedData, units: Units) -> {str: str}:
    """
    Translate Visibility, Altimeter, Clouds, and Other
    """
    data = {}
    data["visibility"] = visibility(wxdata.visibility, units.visibility)
    data["altimeter"] = altimeter(wxdata.altimeter, units.altimeter)
    data["clouds"] = clouds(wxdata.clouds, units.altitude)
    data["wx_codes"] = wx_codes(wxdata.wx_codes)
    return data
