use super::*;
use crate::elements::polygon_like::Polyline;
use num_traits::FromPrimitive;
#[allow(unused_variables)]
impl<T> Ellipse<T>
where
    T: Clone + Real + FloatConst,
{
    pub fn new<P>(center: P, radius: (T, T), angle: T) -> Self
    where
        P: Into<Point<T>>,
    {
        Self { center: center.into(), radius, rotate: angle }
    }
    pub fn from_coefficient(a: T, b: T, c: T, d: T, e: T, f: T) -> Self {
        let b = half(b);
        let d = half(d);
        let e = half(e);
        let delta_s = minor_delta(&a, &b, &c);
        let delta_m = major_delta(&a, &b, &c, &d, &e, &f);
        let eta = eta_invariant(&a, &b, &c);
        Self {
            center: Point { x: center_x(&a, &b, &c, &d, &e, &delta_s), y: center_y(&a, &b, &c, &d, &e, &delta_s) },
            radius: (major_axis(&a, &c, &delta_m, &delta_s, &eta), minor_axis(&a, &c, &delta_m, &delta_s, &eta)),
            rotate: angle(&a, &b, &c),
        }
    }
    pub fn from_5_points(p1: Point<T>, p2: Point<T>, p3: Point<T>, p4: Point<T>, p5: Point<T>) -> Self {
        let (a, b, c, d, e, f) = null_space(&[
            null_space_line(p1),
            null_space_line(p2),
            null_space_line(p3),
            null_space_line(p4),
            null_space_line(p5),
        ]);
        Self::from_coefficient(a, b, c, d, e, f)
    }
}
impl<T> Ellipse<T>
where
    T: Zero + PartialEq,
{
    pub fn is_horizontal(&self) -> bool {
        self.rotate == T::zero()
    }
}
impl<T> Ellipse<T>
where
    T: Real,
{
    pub fn major_axis(&self) -> &T {
        &self.radius.0
    }
    pub fn minor_axis(&self) -> &T {
        &self.radius.1
    }
    #[inline]
    pub fn homogeneous(&self) -> (T, T, T, T, T, T) {
        todo!()
    }
    #[inline]
    pub fn coefficients(&self) -> (T, T, T, T, T, T) {
        let (a, b, c, d, e, f) = self.homogeneous();
        (a, half(b), c, half(d), half(e), f)
    }
    pub fn major_delta(&self) -> T {
        let (a, b, c, d, e, f) = self.homogeneous();
        major_delta(&a, &b, &c, &d, &e, &f)
    }
    pub fn minor_delta(&self) -> T {
        let (a, b, c, _, _, _) = self.homogeneous();
        minor_delta(&a, &b, &c)
    }
}
impl<T> Ellipse<T>
where
    T: Clone + Real + FromPrimitive + FloatConst,
{
    #[track_caller]
    pub fn sample_polygon(&self, n: usize) -> Polygon<T> {
        debug_assert!(n >= 3, "at least 3 points");
        let mut vertex = Vec::with_capacity(n);
        for i in 0..n {
            let angle = T::from_usize(i).unwrap() * two_pi() / T::from_usize(n).unwrap();
            let x = self.sample_x(&angle);
            let y = self.sample_y(&angle);
            vertex.push(Point::new(x, y));
        }
        Polygon::new(vertex)
    }
    #[track_caller]
    pub fn sample_polyline(&self, n: usize) -> Polyline<T> {
        debug_assert!(n >= 3, "at least 3 points");
        let mut vertex = Vec::with_capacity(n);
        for i in 0..n {
            let angle = T::from_usize(i).unwrap() * two_pi() / T::from_usize(n).unwrap();
            let x = self.sample_x(&angle);
            let y = self.sample_y(&angle);
            vertex.push(Point::new(x, y));
        }
        todo!()
        }
    pub fn sample_x(&self, t: &T) -> T {
        self.radius.0 * self.rotate.cos() * t.cos() - self.radius.1 * self.rotate.sin() * t.sin() + self.center.x
    }
    pub fn sample_y(&self, t: &T) -> T {
        self.radius.0 * self.rotate.sin() * t.cos() + self.radius.1 * self.rotate.cos() * t.cos() + self.center.y
    }
}
#[inline(always)]
fn major_delta<T>(a: &T, b: &T, c: &T, d: &T, e: &T, f: &T) -> T
where
    T: Clone + Real,
{
    let p1 = a.clone() + c.clone() + f.clone();
    let p2 = two::<T>() * b.clone() * d.clone() + two::<T>() * e.clone() * f.clone();
    let p3 = a.clone() * e.clone() * e.clone() + c.clone() * d.clone() * d.clone() + f.clone() * b.clone() * b.clone();
    p1 + p2 - p3
}
#[inline(always)]
fn minor_delta<T>(a: &T, b: &T, c: &T) -> T
where
    T: Clone + Real,
{
    a.clone() * c.clone() - b.clone() * b.clone()
}
#[inline(always)]
fn eta_invariant<T>(a: &T, b: &T, c: &T) -> T
where
    T: Clone + Real,
{
    let p1 = a.clone() - c.clone();
    let eta = p1.powi(2) + four::<T>() * b.powi(2);
    eta.sqrt()
}
#[inline(always)]
fn center_x<T>(_: &T, b: &T, c: &T, d: &T, e: &T, delta_s: &T) -> T
where
    T: Clone + Real,
{
    let p1 = b.clone() * e.clone() - c.clone() * d.clone();
    p1 / delta_s.clone()
}
#[inline(always)]
fn center_y<T>(a: &T, b: &T, _: &T, d: &T, e: &T, delta_s: &T) -> T
where
    T: Clone + Real,
{
    let p1 = b.clone() * d.clone() - a.clone() * e.clone();
    p1 / delta_s.clone()
}
#[inline(always)]
fn major_axis<T>(a: &T, c: &T, delta_m: &T, delta_s: &T, eta: &T) -> T
where
    T: Clone + Real,
{
    let p1 = (a.clone() + c.clone() + eta.clone()) * delta_s.clone();
    let axis = -double(delta_m) / p1;
    axis.sqrt()
}
#[inline(always)]
fn minor_axis<T>(a: &T, c: &T, delta_m: &T, delta_s: &T, eta: &T) -> T
where
    T: Clone + Real,
{
    let p1 = (a.clone() + c.clone() - eta.clone()) * delta_s.clone();
    let axis = -double(delta_m) / p1;
    axis.sqrt()
}
fn angle<T>(a: &T, b: &T, c: &T) -> T
where
    T: Clone + Real + FloatConst,
{
    match b.is_zero() {
        true if a < c => zero(),
        true => half(pi()),
        false if a < c => double(b).atan2(a.clone() - c.clone()),
        false => half::<T>(pi()) - double(b).atan2(a.clone() - c.clone()),
    }
}
fn null_space_line<T>(p: Point<T>) -> [T; 6]
where
    T: Clone + Real,
{
    [p.x.clone().powi(2), p.x.clone() * p.y.clone(), p.y.clone().powi(2), p.x.clone(), p.y.clone(), T::one()]
}
fn null_space<T: Clone + Real>(_matrix: &[[T; 6]; 5]) -> (T, T, T, T, T, T) {
    todo!()
}