use std::fmt;

use serde::{Deserialize, Serialize};
#[cfg(feature = "bindings")]
use ts_rs::TS;

use crate::geo::{Degrees, FloatMeters};

#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "bindings", derive(TS))]
#[cfg_attr(feature = "bindings", ts(export))]
pub struct Turnpoint {
    // The rename directive needs to be split from other serde directives to prevent
    // ts-rs from failing to parse this attribute.
    #[serde(rename = "type")]
    #[serde(
        default,
        skip_serializing_if = "TurnpointRole::is_skipped_during_serialization"
    )]
    pub role: TurnpointRole,
    pub radius: FloatMeters,
    pub waypoint: Waypoint,
}

#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "bindings", derive(TS))]
#[cfg_attr(feature = "bindings", ts(export))]
pub struct Waypoint {
    pub name: String,
    pub description: Option<String>,
    pub lat: Degrees,
    pub lon: Degrees,
    #[serde(rename = "altSmoothed")]
    pub alt: FloatMeters,
}

#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "bindings", derive(TS))]
#[cfg_attr(feature = "bindings", ts(export))]
pub enum TurnpointRole {
    #[serde(rename = "TAKEOFF")]
    Takeoff,
    #[serde(rename = "SSS")]
    Sss,
    #[serde(rename = "ESS")]
    Ess,
    #[serde(rename(serialize = "ESS"))]
    EssAndGoal,
    #[default]
    Turnpoint,
    Goal,
}

impl TurnpointRole {
    fn is_skipped_during_serialization(&self) -> bool {
        matches!(self, TurnpointRole::Turnpoint | TurnpointRole::Goal)
    }
}

impl fmt::Display for TurnpointRole {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TurnpointRole::Takeoff => write!(f, "TAKEOFF"),
            TurnpointRole::Sss => write!(f, "SSS"),
            TurnpointRole::Turnpoint => write!(f, "TP"),
            TurnpointRole::Ess => write!(f, "ESS"),
            TurnpointRole::EssAndGoal => write!(f, "ESS+GOAL"),
            TurnpointRole::Goal => write!(f, "GOAL"),
        }
    }
}
