use serde::{Deserialize, Serialize};

use crate::{FeatureId, SketchEntityId};

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct LoopIndex(u16);

impl LoopIndex {
    pub const OUTER: Self = Self(0);

    #[must_use]
    pub const fn new(value: u16) -> Self {
        Self(value)
    }

    #[must_use]
    pub const fn value(self) -> u16 {
        self.0
    }
}

impl core::fmt::Display for LoopIndex {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "loop#{}", self.0)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ImportOrdinal(u32);

impl ImportOrdinal {
    #[must_use]
    pub const fn new(value: u32) -> Self {
        Self(value)
    }

    #[must_use]
    pub const fn value(self) -> u32 {
        self.0
    }
}

impl core::fmt::Display for ImportOrdinal {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "import#{}", self.0)
    }
}

#[derive(
    Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct BodyOrdinal(u16);

impl BodyOrdinal {
    #[must_use]
    pub const fn new(value: u16) -> Self {
        Self(value)
    }

    #[must_use]
    pub const fn origin() -> Self {
        Self(0)
    }

    #[must_use]
    pub const fn value(self) -> u16 {
        self.0
    }

    #[must_use]
    pub const fn is_origin(&self) -> bool {
        self.0 == 0
    }
}

impl core::fmt::Display for BodyOrdinal {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "body#{}", self.0)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct CutKey([u64; 3]);

impl CutKey {
    #[must_use]
    pub const fn new(bits: [u64; 3]) -> Self {
        Self(bits)
    }

    #[must_use]
    pub const fn bits(self) -> [u64; 3] {
        self.0
    }
}

impl core::fmt::Display for CutKey {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{:x}:{:x}:{:x}", self.0[0], self.0[1], self.0[2])
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum SideKind {
    Corner,
    Seam,
}

impl SideKind {
    #[must_use]
    pub const fn label(self) -> &'static str {
        match self {
            Self::Corner => "corner",
            Self::Seam => "seam",
        }
    }
}

impl core::fmt::Display for SideKind {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(self.label())
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum FaceRole {
    StartCap,
    Side {
        loop_index: LoopIndex,
        from: SketchEntityId,
    },
    EndCap,
    Imported {
        ordinal: ImportOrdinal,
    },
}

impl core::fmt::Display for FaceRole {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::StartCap => f.write_str("start_cap"),
            Self::Side { loop_index, from } => write!(f, "side({loop_index}, from={from:?})"),
            Self::EndCap => f.write_str("end_cap"),
            Self::Imported { ordinal } => write!(f, "imported({ordinal})"),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum EdgeRole {
    StartCapEdge {
        from: SketchEntityId,
    },
    SideEdge {
        from: SketchEntityId,
        side: SideKind,
    },
    EndCapEdge {
        from: SketchEntityId,
    },
    Intersection {
        a: FaceLabel,
        b: FaceLabel,
    },
    Imported {
        ordinal: ImportOrdinal,
    },
}

impl EdgeRole {
    #[must_use]
    pub fn intersection(a: FaceLabel, b: FaceLabel) -> Self {
        let (a, b) = if a <= b { (a, b) } else { (b, a) };
        Self::Intersection { a, b }
    }
}

impl core::fmt::Display for EdgeRole {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::StartCapEdge { from } => write!(f, "start_cap_edge(from={from:?})"),
            Self::SideEdge { from, side } => write!(f, "side_edge({side}, from={from:?})"),
            Self::EndCapEdge { from } => write!(f, "end_cap_edge(from={from:?})"),
            Self::Intersection { a, b } => write!(f, "intersection({a}, {b})"),
            Self::Imported { ordinal } => write!(f, "imported({ordinal})"),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum VertexRole {
    StartCapVertex {
        from: SketchEntityId,
        side: SideKind,
    },
    EndCapVertex {
        from: SketchEntityId,
        side: SideKind,
    },
    Seam {
        from: SketchEntityId,
    },
    Intersection {
        faces: [FaceLabel; 3],
    },
    Cut {
        key: CutKey,
    },
    Imported {
        ordinal: ImportOrdinal,
    },
}

impl core::fmt::Display for VertexRole {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::StartCapVertex { from, side } => {
                write!(f, "start_cap_vertex({side}, from={from:?})")
            }
            Self::EndCapVertex { from, side } => {
                write!(f, "end_cap_vertex({side}, from={from:?})")
            }
            Self::Seam { from } => write!(f, "seam(from={from:?})"),
            Self::Intersection { faces: [a, b, c] } => {
                write!(f, "intersection({a}, {b}, {c})")
            }
            Self::Cut { key } => write!(f, "cut({key})"),
            Self::Imported { ordinal } => write!(f, "imported({ordinal})"),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FaceLabel {
    pub feature: FeatureId,
    pub role: FaceRole,
}

impl core::fmt::Display for FaceLabel {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "face[{:?}]:{}", self.feature, self.role)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EdgeLabel {
    pub feature: FeatureId,
    pub role: EdgeRole,
}

impl core::fmt::Display for EdgeLabel {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "edge[{:?}]:{}", self.feature, self.role)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VertexLabel {
    pub feature: FeatureId,
    pub role: VertexRole,
}

impl core::fmt::Display for VertexLabel {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "vertex[{:?}]:{}", self.feature, self.role)
    }
}
