#[cfg(test)]
use bone_types::Vec3;
use bone_types::{Point3, Tolerance, UnitVec3};
use truck_modeling::{Point3 as TruckPoint3, Vector3 as TruckVector3};

pub(crate) fn point_from_truck(p: TruckPoint3) -> Point3 {
    Point3::from_mm(p.x, p.y, p.z)
}

pub(crate) fn try_unit_from_truck(v: TruckVector3, tolerance: Tolerance) -> Option<UnitVec3> {
    UnitVec3::try_from_components(v.x, v.y, v.z, tolerance).ok()
}

#[cfg(test)]
pub(crate) fn point_to_truck(p: Point3) -> TruckPoint3 {
    let (x, y, z) = p.coords_mm();
    TruckPoint3::new(x, y, z)
}

#[cfg(test)]
pub(crate) fn vec_from_truck(v: TruckVector3) -> Vec3 {
    Vec3::from_mm(v.x, v.y, v.z)
}

#[cfg(test)]
pub(crate) fn vec_to_truck(v: Vec3) -> TruckVector3 {
    let (x, y, z) = v.coords_mm();
    TruckVector3::new(x, y, z)
}

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;

    fn finite_coord() -> impl Strategy<Value = f64> {
        prop::num::f64::POSITIVE | prop::num::f64::NEGATIVE | prop::num::f64::ZERO
    }

    proptest! {
        #[test]
        fn point_roundtrips_to_ulp(
            x in finite_coord(),
            y in finite_coord(),
            z in finite_coord(),
        ) {
            let bone = Point3::from_mm(x, y, z);
            let back = point_from_truck(point_to_truck(bone));
            let (bx, by, bz) = back.coords_mm();
            prop_assert!(bx.to_bits() == x.to_bits());
            prop_assert!(by.to_bits() == y.to_bits());
            prop_assert!(bz.to_bits() == z.to_bits());
        }

        #[test]
        fn vec_roundtrips_to_ulp(
            x in finite_coord(),
            y in finite_coord(),
            z in finite_coord(),
        ) {
            let bone = Vec3::from_mm(x, y, z);
            let back = vec_from_truck(vec_to_truck(bone));
            let (bx, by, bz) = back.coords_mm();
            prop_assert!(bx.to_bits() == x.to_bits());
            prop_assert!(by.to_bits() == y.to_bits());
            prop_assert!(bz.to_bits() == z.to_bits());
        }
    }
}
