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 Circle {
    pub core: ObjectCore,
    #[brw(magic(6u32))] // Number of following fields in struct
    pub drawn_origin: FieldOf<Point>,
    pub radius: F64,
    pub start_angle: F64, // Radians
    pub clockwise: U32,
    pub modifier: ObjectModifier,
}

impl Display for Circle {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (origin, width, height) = self.modifier.corrected(
            *self.drawn_origin - Point::from(*self.radius),
            *self.drawn_origin + Point::from(*self.radius),
        );
        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$}, Clockwise: {}",
            self.core,
            origin,
            width,
            height,
            *self.start_angle,
            *self.clockwise != 0,
        )
    }
}

impl Default for Circle {
    fn default() -> Self {
        Self {
            core: ObjectCore::default(ObjectType::Circle),
            drawn_origin: Point { x: 0.0, y: 0.0 }.into(),
            radius: 0.0.into(),
            start_angle: 0.0.into(),
            clockwise: 0.into(),
            modifier: ObjectModifier::default(),
        }
    }
}

// origin_x = x_correction + drawn_x * x_scale
// x_correction = origin_x - drawn_x * x_scale
impl Translate for Circle {
    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);
    }
}
