use std::fmt::{Debug, Display};

use binrw::{BinRead, BinWrite};
use diff::Diff;
use log::warn;

use crate::{
    field_of::FieldOf,
    types::{ObjectType, Point, F64, U32},
    FP,
};

use super::{ObjectCore, ObjectModifier, Translate};

#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
pub struct Ellipse {
    pub core: ObjectCore,
    #[brw(magic(8u32))] // Number of following fields in struct
    pub clockwise: U32,
    pub drawn_corner_a: FieldOf<Point>,
    pub drawn_corner_b: FieldOf<Point>,
    pub start_angle: F64, // Radians
    pub end_angle: F64,   // Radians
    pub modifier: ObjectModifier,
    pub open_curve: U32,
}

impl Display for Ellipse {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (origin, width, height) = self
            .modifier
            .corrected(*self.drawn_corner_a, *self.drawn_corner_b);

        if origin != *self.core.origin {
            warn!(
                "Origin mismatch! Core: {:.FP$}, Calculated: {:.FP$}",
                *self.core.origin, origin
            );
        }

        write!(
            f,
            "{}, Origin: {}, Width: {:.FP$}, Height: {:.FP$}, Start Radian: {:.FP$}, End Radian: {:.FP$}, Open Curve: {}", 
            self.core,
            origin,
            width,
            height,
                *self.start_angle,
                *self.end_angle,
                *self.open_curve != 0,
        )?;
        write!(f, "\n{:?}", self.modifier)?;
        Ok(())
    }
}

impl Default for Ellipse {
    fn default() -> Self {
        Self {
            core: ObjectCore::default(ObjectType::Ellipse),
            clockwise: 0.into(),
            drawn_corner_a: Point { x: 0.0, y: 0.0 }.into(),
            drawn_corner_b: Point { x: 0.0, y: 0.0 }.into(),
            start_angle: 0.0.into(),
            end_angle: 0.0.into(),
            modifier: ObjectModifier::default(),
            open_curve: 0.into(),
        }
    }
}

impl Translate for Ellipse {
    fn move_absolute(&mut self, origin: Option<Point>, z: Option<f64>) {
        origin.map(|origin| {
            let delta: Point = origin - *self.core.origin;
            self.modifier.move_relative(delta);
        });
        self.core.move_absolute(origin, z);
    }

    fn move_relative(&mut self, delta: Option<Point>, z: Option<f64>) {
        delta.map(|delta| {
            self.modifier.move_relative(delta);
        });
        self.core.move_relative(delta, z);
    }
}
