use bone_types::{Aabb3, ChordHeightTolerance, Parameter, Point3, Tolerance, Vec3};

use crate::arc3::Arc3;
use crate::circle3::Circle3;
use crate::closest::ClosestPoint3;
use crate::line3::Line3;

pub trait Curve3 {
    fn evaluate(&self, t: Parameter) -> Point3;
    fn derivative(&self, t: Parameter) -> Vec3;
    fn bounding_box(&self) -> Aabb3;
    fn closest_point(&self, p: Point3, tolerance: Tolerance) -> ClosestPoint3;
    fn tessellate(&self, tolerance: ChordHeightTolerance) -> Vec<Point3>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Curve3Kind {
    Line(Line3),
    Arc(Arc3),
    Circle(Circle3),
}

impl Curve3 for Curve3Kind {
    fn evaluate(&self, t: Parameter) -> Point3 {
        match self {
            Self::Line(c) => c.evaluate(t),
            Self::Arc(c) => c.evaluate(t),
            Self::Circle(c) => c.evaluate(t),
        }
    }
    fn derivative(&self, t: Parameter) -> Vec3 {
        match self {
            Self::Line(c) => c.derivative(t),
            Self::Arc(c) => c.derivative(t),
            Self::Circle(c) => c.derivative(t),
        }
    }
    fn bounding_box(&self) -> Aabb3 {
        match self {
            Self::Line(c) => c.bounding_box(),
            Self::Arc(c) => c.bounding_box(),
            Self::Circle(c) => c.bounding_box(),
        }
    }
    fn closest_point(&self, p: Point3, tolerance: Tolerance) -> ClosestPoint3 {
        match self {
            Self::Line(c) => c.closest_point(p, tolerance),
            Self::Arc(c) => c.closest_point(p, tolerance),
            Self::Circle(c) => c.closest_point(p, tolerance),
        }
    }
    fn tessellate(&self, tolerance: ChordHeightTolerance) -> Vec<Point3> {
        match self {
            Self::Line(c) => c.tessellate(tolerance),
            Self::Arc(c) => c.tessellate(tolerance),
            Self::Circle(c) => c.tessellate(tolerance),
        }
    }
}
