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

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

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

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

#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
pub struct Rectangle {
    pub core: ObjectCore,
    #[brw(magic(8u32))] // Number of following fields in struct
    pub drawn_corner_a: FieldOf<Point>,
    pub drawn_corner_b: FieldOf<Point>,
    pub round_bottom_left: F64,
    pub round_bottom_right: F64,
    pub round_top_right: F64,
    pub round_top_left: F64,
    pub modifier: ObjectModifier,
}

impl Display for Rectangle {
    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$}",
            self.core, origin, width, height,
        )
    }
}

impl Default for Rectangle {
    fn default() -> Self {
        Self {
            core: ObjectCore::default(ObjectType::Rectangle),
            drawn_corner_a: Point { x: 0.0, y: 0.0 }.into(),
            drawn_corner_b: Point { x: 0.0, y: 0.0 }.into(),
            round_bottom_left: 0.0.into(),
            round_bottom_right: 0.0.into(),
            round_top_right: 0.0.into(),
            round_top_left: 0.0.into(),
            modifier: ObjectModifier::default(),
        }
    }
}

// origin_x = x_corretion + (drawn_a.x + drawn_b.x) / 2 * x_scale
// x_correction = origin_x - (drawn_a.x + drawn_b.x) / 2 * x_scale
impl Translate for Rectangle {
    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);
    }
}
