use bone_types::Length;
use uom::si::length::millimeter;

use crate::cone_surface::ConeSurface;
use crate::cylinder_surface::CylinderSurface;
use crate::plane_surface::PlaneSurface;
use crate::sphere_surface::SphereSurface;
use crate::torus_surface::TorusSurface;

pub mod blend;
pub mod offset;
pub mod patch;
pub mod trim;

pub use blend::{BlendGap, BlendRadius, BlendSpine, NormalSide, rolling_ball_blend};
pub use offset::{OffsetDegeneracy, OffsetDistance, OffsetResult, offset_surface};
pub use patch::{FillContinuity, PatchGap, fill_patch};
pub use trim::{TrimGap, carrier, face_boundary, trim_line_to_face};

#[must_use]
fn mm(value: f64) -> Length {
    Length::new::<millimeter>(value)
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AnalyticPatch {
    Plane(PlaneSurface),
    Cylinder(CylinderSurface),
    Cone(ConeSurface),
    Sphere(SphereSurface),
    Torus(TorusSurface),
}

impl AnalyticPatch {
    #[must_use]
    pub fn min_radius_of_curvature(self) -> Option<Length> {
        match self {
            Self::Plane(_) => None,
            Self::Cylinder(cylinder) => Some(cylinder.radius()),
            Self::Cone(cone) => Some(cone.min_radius_of_curvature()),
            Self::Sphere(sphere) => Some(sphere.radius()),
            Self::Torus(torus) => {
                let minor = torus.minor_radius_mm();
                let inner = torus.major_radius_mm() - minor;
                Some(Length::new::<millimeter>(minor.min(inner)))
            }
        }
    }

    #[must_use]
    fn outward_sign(self) -> f64 {
        match self {
            Self::Plane(_) => 1.0,
            Self::Cylinder(cylinder) => cylinder.orientation(),
            Self::Cone(cone) => cone.orientation(),
            Self::Sphere(sphere) => sphere.orientation(),
            Self::Torus(torus) => torus.orientation(),
        }
    }
}
