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 Polygon {
    pub core: ObjectCore,
    #[brw(magic(10u32))] // Number of following fields in struct
    pub invert_shape: U32,
    pub drawn_corner_a: FieldOf<Point>,
    pub drawn_corner_b: FieldOf<Point>,
    pub offset_cx: F64,
    pub offset_cy: F64,
    pub offset_dx: F64,
    pub offset_dy: F64,
    pub edges: U32,
    pub modifier: ObjectModifier,
}

impl Display for Polygon {
    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$}, Inverted: {}, Offset CX: {:.FP$}, Offset CY: {:.FP$}, Offset DX: {:.FP$}, Offset: DY: {:.FP$}, Edges: {}",
            self.core,
            origin,
            width,
            height,
            *self.invert_shape != 0,
            *self.offset_cx,
            *self.offset_cy,
            *self.offset_dx,
            *self.offset_dy,
            *self.edges,
        )
    }
}

impl Default for Polygon {
    fn default() -> Self {
        Self {
            core: ObjectCore::default(ObjectType::Polygon),
            invert_shape: 1.into(),
            drawn_corner_a: Point { x: 0.0, y: 0.0 }.into(),
            drawn_corner_b: Point { x: 0.0, y: 0.0 }.into(),
            offset_cx: 0.0.into(),
            offset_cy: 0.0.into(),
            offset_dx: 0.0.into(),
            offset_dy: 0.0.into(),
            edges: 6.into(),
            modifier: ObjectModifier::default(),
        }
    }
}

impl Translate for Polygon {
    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);
    }
}
