use std::collections::HashMap;

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum PresetColor {
    #[serde(rename = "1")]
    Red = 1,
    #[serde(rename = "2")]
    Orange = 2,
    #[serde(rename = "3")]
    Yellow = 3,
    #[serde(rename = "4")]
    Green = 4,
    #[serde(rename = "5")]
    Cyan = 5,
    #[serde(rename = "6")]
    Purple = 6,
}

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Color {
    Preset(PresetColor),
    Color(HexColor),
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(transparent)]
#[serde(transparent)]
pub struct EdgeId(pub(self) String);

#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(transparent)]
#[serde(transparent)]
pub struct NodeId(pub(self) String);

#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct JsonCanvas {
    #[serde(
        serialize_with = "serialize_as_vec_node",
        deserialize_with = "deserialize_as_map_node"
    )]
    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
    nodes: HashMap<NodeId, Node>,

    #[serde(
        serialize_with = "serialize_as_vec_edge",
        deserialize_with = "deserialize_as_map_edge"
    )]
    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
    edges: HashMap<EdgeId, Edge>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Edge {
    pub id: EdgeId,

    #[serde(skip_serializing_if = "Option::is_none")]
    color: Option<Color>,
    #[serde(skip_serializing_if = "Option::is_none")]
    label: Option<String>,

    pub from_node: NodeId,
    pub to_node: NodeId,

    #[serde(skip_serializing_if = "Option::is_none")]
    from_side: Option<EdgeSide>,
    #[serde(skip_serializing_if = "Option::is_none")]
    to_side: Option<EdgeSide>,

    #[serde(skip_serializing_if = "Option::is_none")]
    from_end: Option<EdgeEnd>,
    #[serde(skip_serializing_if = "Option::is_none")]
    to_end: Option<EdgeEnd>,
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EdgeSide {
    Top,
    Left,
    Right,
    Bottom,
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EdgeEnd {
    None,
    Arrow,
}
