use crate::geometry::Point;
use std::fmt;
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Ellipse {
    pub center: Point,
    pub semi_major: f64,
    pub semi_minor: f64,
    pub rotation: f64,
}

impl Ellipse {
    #[inline]
    pub fn new(center: Point, semi_major: f64, semi_minor: f64, rotation: f64) -> Self {
        Self {
            center,
            semi_major,
            semi_minor,
            rotation,
        }
    }

    #[inline]
    pub fn eccentricity(&self) -> f64 {
        if self.semi_major == 0.0 {
            0.0
        } else {
            (1.0 - (self.semi_minor / self.semi_major).powi(2)).sqrt()
        }
    }

    #[inline]
    pub fn area(&self) -> f64 {
        std::f64::consts::PI * self.semi_major * self.semi_minor
    }

    #[inline]
    pub fn circumference_approx(&self) -> f64 {
        let a = self.semi_major;
        let b = self.semi_minor;
        std::f64::consts::PI * (3.0 * (a + b) - ((3.0 * a + b) * (a + 3.0 * b)).sqrt())
    }

    #[inline]
    pub fn point_at_parameter(&self, t: f64) -> Point {
        let angle = t * 2.0 * std::f64::consts::PI;
        let cos_angle = angle.cos();
        let sin_angle = angle.sin();
        let cos_rot = self.rotation.cos();
        let sin_rot = self.rotation.sin();

        let local_x = self.semi_major * cos_angle;
        let local_y = self.semi_minor * sin_angle;

        Point::new(
            self.center.x + local_x * cos_rot - local_y * sin_rot,
            self.center.y + local_x * sin_rot + local_y * cos_rot,
            self.center.z,
        )
    }

    #[inline]
    pub fn contains_point(&self, p: &Point) -> bool {
        let dx = p.x - self.center.x;
        let dy = p.y - self.center.y;
        let cos_rot = self.rotation.cos();
        let sin_rot = self.rotation.sin();

        let local_x = dx * cos_rot + dy * sin_rot;
        let local_y = -dx * sin_rot + dy * cos_rot;

        ((local_x / self.semi_major).powi(2) + (local_y / self.semi_minor).powi(2)) <= 1.0 + 1e-10
    }

    #[inline]
    pub fn major_axis(&self) -> f64 {
        self.semi_major * 2.0
    }

    #[inline]
    pub fn minor_axis(&self) -> f64 {
        self.semi_minor * 2.0
    }

    #[inline]
    pub fn center(&self) -> Point {
        self.center
    }
}

impl fmt::Display for Ellipse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Ellipse(center: {}, semi_major: {}, semi_minor: {}, rotation: {:.2}°)",
            self.center,
            self.semi_major,
            self.semi_minor,
            self.rotation.to_degrees()
        )
    }
}

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

    #[test]
    fn test_ellipse_creation() {
        let ellipse = Ellipse::new(Point::origin(), 5.0, 3.0, 0.0);
        
        assert_eq!(ellipse.semi_major, 5.0);
        assert_eq!(ellipse.semi_minor, 3.0);
    }

    #[test]
    fn test_ellipse_eccentricity() {
        let ellipse = Ellipse::new(Point::origin(), 5.0, 3.0, 0.0);
        let ecc = ellipse.eccentricity();
        
        assert!(ecc > 0.0 && ecc < 1.0);
    }

    #[test]
    fn test_ellipse_area() {
        let ellipse = Ellipse::new(Point::origin(), 5.0, 3.0, 0.0);
        let area = ellipse.area();
        
        assert!((area - 15.0 * std::f64::consts::PI).abs() < 1e-10);
    }
}