use bone_types::{Length, Point2, SketchEntityId, dimensioned_serde};
use serde::{Deserialize, Serialize};

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PointData {
    at: Point2,
}

impl PointData {
    #[must_use]
    pub const fn new(at: Point2) -> Self {
        Self { at }
    }

    #[must_use]
    pub const fn at(self) -> Point2 {
        self.at
    }

    #[must_use]
    pub const fn for_construction(self) -> bool {
        true
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LineData {
    a: SketchEntityId,
    b: SketchEntityId,
    for_construction: bool,
}

impl LineData {
    #[must_use]
    pub const fn new(a: SketchEntityId, b: SketchEntityId, for_construction: bool) -> Self {
        Self {
            a,
            b,
            for_construction,
        }
    }

    #[must_use]
    pub const fn a(self) -> SketchEntityId {
        self.a
    }

    #[must_use]
    pub const fn b(self) -> SketchEntityId {
        self.b
    }

    #[must_use]
    pub const fn for_construction(self) -> bool {
        self.for_construction
    }

    #[must_use]
    pub const fn with_construction(self, for_construction: bool) -> Self {
        Self {
            for_construction,
            ..self
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ArcData {
    center: SketchEntityId,
    start: SketchEntityId,
    end: SketchEntityId,
    for_construction: bool,
}

impl ArcData {
    #[must_use]
    pub const fn new(
        center: SketchEntityId,
        start: SketchEntityId,
        end: SketchEntityId,
        for_construction: bool,
    ) -> Self {
        Self {
            center,
            start,
            end,
            for_construction,
        }
    }

    #[must_use]
    pub const fn center(self) -> SketchEntityId {
        self.center
    }

    #[must_use]
    pub const fn start(self) -> SketchEntityId {
        self.start
    }

    #[must_use]
    pub const fn end(self) -> SketchEntityId {
        self.end
    }

    #[must_use]
    pub const fn for_construction(self) -> bool {
        self.for_construction
    }

    #[must_use]
    pub const fn with_construction(self, for_construction: bool) -> Self {
        Self {
            for_construction,
            ..self
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CircleData {
    center: SketchEntityId,
    #[serde(with = "dimensioned_serde::length_si")]
    radius: Length,
    for_construction: bool,
}

impl CircleData {
    #[must_use]
    pub const fn new(center: SketchEntityId, radius: Length, for_construction: bool) -> Self {
        Self {
            center,
            radius,
            for_construction,
        }
    }

    #[must_use]
    pub const fn center(self) -> SketchEntityId {
        self.center
    }

    #[must_use]
    pub const fn radius(self) -> Length {
        self.radius
    }

    #[must_use]
    pub const fn for_construction(self) -> bool {
        self.for_construction
    }

    #[must_use]
    pub const fn with_construction(self, for_construction: bool) -> Self {
        Self {
            for_construction,
            ..self
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EllipseData {
    center: SketchEntityId,
    major: SketchEntityId,
    minor: SketchEntityId,
    for_construction: bool,
}

impl EllipseData {
    #[must_use]
    pub const fn new(
        center: SketchEntityId,
        major: SketchEntityId,
        minor: SketchEntityId,
        for_construction: bool,
    ) -> Self {
        Self {
            center,
            major,
            minor,
            for_construction,
        }
    }

    #[must_use]
    pub const fn center(self) -> SketchEntityId {
        self.center
    }

    #[must_use]
    pub const fn major(self) -> SketchEntityId {
        self.major
    }

    #[must_use]
    pub const fn minor(self) -> SketchEntityId {
        self.minor
    }

    #[must_use]
    pub const fn for_construction(self) -> bool {
        self.for_construction
    }

    #[must_use]
    pub const fn with_construction(self, for_construction: bool) -> Self {
        Self {
            for_construction,
            ..self
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SplineData {
    control: Vec<SketchEntityId>,
    #[serde(default)]
    closed: bool,
    #[serde(default)]
    for_construction: bool,
}

impl SplineData {
    #[must_use]
    pub fn new(control: Vec<SketchEntityId>, closed: bool, for_construction: bool) -> Self {
        Self {
            control,
            closed,
            for_construction,
        }
    }

    #[must_use]
    pub fn control(&self) -> &[SketchEntityId] {
        &self.control
    }

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

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

    #[must_use]
    pub fn with_construction(&self, for_construction: bool) -> Self {
        Self {
            control: self.control.clone(),
            closed: self.closed,
            for_construction,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SketchEntityKind {
    Point,
    Line,
    Arc,
    Circle,
    Ellipse,
    Spline,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum SketchEntity {
    Point(PointData),
    Line(LineData),
    Arc(ArcData),
    Circle(CircleData),
    Ellipse(EllipseData),
    Spline(SplineData),
}

impl SketchEntity {
    #[must_use]
    pub const fn point(at: Point2) -> Self {
        Self::Point(PointData::new(at))
    }

    #[must_use]
    pub const fn line(a: SketchEntityId, b: SketchEntityId, for_construction: bool) -> Self {
        Self::Line(LineData::new(a, b, for_construction))
    }

    #[must_use]
    pub const fn arc(
        center: SketchEntityId,
        start: SketchEntityId,
        end: SketchEntityId,
        for_construction: bool,
    ) -> Self {
        Self::Arc(ArcData::new(center, start, end, for_construction))
    }

    #[must_use]
    pub const fn circle(center: SketchEntityId, radius: Length, for_construction: bool) -> Self {
        Self::Circle(CircleData::new(center, radius, for_construction))
    }

    #[must_use]
    pub const fn ellipse(
        center: SketchEntityId,
        major: SketchEntityId,
        minor: SketchEntityId,
        for_construction: bool,
    ) -> Self {
        Self::Ellipse(EllipseData::new(center, major, minor, for_construction))
    }

    #[must_use]
    pub fn spline(control: Vec<SketchEntityId>, closed: bool, for_construction: bool) -> Self {
        Self::Spline(SplineData::new(control, closed, for_construction))
    }

    #[must_use]
    pub fn for_construction(&self) -> bool {
        match self {
            Self::Point(p) => p.for_construction(),
            Self::Line(l) => l.for_construction(),
            Self::Arc(a) => a.for_construction(),
            Self::Circle(c) => c.for_construction(),
            Self::Ellipse(e) => e.for_construction(),
            Self::Spline(s) => s.for_construction(),
        }
    }

    #[must_use]
    pub const fn is_point(&self) -> bool {
        matches!(self, Self::Point(_))
    }

    #[must_use]
    pub const fn kind(&self) -> SketchEntityKind {
        match self {
            Self::Point(_) => SketchEntityKind::Point,
            Self::Line(_) => SketchEntityKind::Line,
            Self::Arc(_) => SketchEntityKind::Arc,
            Self::Circle(_) => SketchEntityKind::Circle,
            Self::Ellipse(_) => SketchEntityKind::Ellipse,
            Self::Spline(_) => SketchEntityKind::Spline,
        }
    }

    #[must_use]
    pub fn references(&self) -> EntityRefs {
        EntityRefs(match self {
            Self::Point(_) => Vec::new(),
            Self::Line(l) => vec![l.a(), l.b()],
            Self::Arc(a) => vec![a.center(), a.start(), a.end()],
            Self::Circle(c) => vec![c.center()],
            Self::Ellipse(e) => vec![e.center(), e.major(), e.minor()],
            Self::Spline(s) => s.control().to_vec(),
        })
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct EntityRefs(Vec<SketchEntityId>);

impl IntoIterator for EntityRefs {
    type Item = SketchEntityId;
    type IntoIter = std::vec::IntoIter<SketchEntityId>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}
