#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
pub struct Parameter(f64);

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

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

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

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

impl DegreesOfFreedom {
    #[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 DegreesOfFreedom {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "dof={}", self.0)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SolverSeed(u64);

impl SolverSeed {
    pub const DEFAULT: Self = Self(0x5E61_D000_7E57_5EEDu64);

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

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

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

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

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

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

    #[must_use]
    pub const fn as_usize(self) -> usize {
        self.0 as usize
    }
}

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

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

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

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

    #[must_use]
    pub const fn as_usize(self) -> usize {
        self.0 as usize
    }
}

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

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

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

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

    #[must_use]
    pub const fn as_usize(self) -> usize {
        self.0 as usize
    }

    #[must_use]
    pub const fn next(self) -> Self {
        Self(self.0.saturating_add(1))
    }
}

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

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SketchItemId {
    Relation(crate::SketchRelationId),
    Dimension(crate::SketchDimensionId),
    Entity(crate::SketchEntityId),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SketchStatus {
    UnderDefined,
    FullyDefined,
    OverDefined,
    NoSolutionFound,
    InvalidSolutionFound,
    Dangling,
}

impl SketchStatus {
    #[must_use]
    pub const fn label(self) -> &'static str {
        match self {
            Self::UnderDefined => "Under Defined",
            Self::FullyDefined => "Fully Defined",
            Self::OverDefined => "Over Defined",
            Self::NoSolutionFound => "No Solution Found",
            Self::InvalidSolutionFound => "Invalid Solution Found",
            Self::Dangling => "Dangling",
        }
    }
}

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

impl core::fmt::Display for SketchItemId {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Relation(id) => write!(f, "relation({id:?})"),
            Self::Dimension(id) => write!(f, "dimension({id:?})"),
            Self::Entity(id) => write!(f, "entity({id:?})"),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct SolverResidual(f64);

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

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

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

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct NewtonDamping(f64);

impl NewtonDamping {
    pub const DEFAULT: Self = Self(1e-12);

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

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

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

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct NewtonStepTolerance(f64);

impl NewtonStepTolerance {
    pub const DEFAULT: Self = Self(1e-14);

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

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

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

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BudgetCeiling(core::time::Duration);

impl BudgetCeiling {
    pub const FRAME_16MS: Self = Self(core::time::Duration::from_millis(16));

    #[must_use]
    pub const fn new(duration: core::time::Duration) -> Self {
        Self(duration)
    }

    #[must_use]
    pub const fn duration(self) -> core::time::Duration {
        self.0
    }
}

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