use std::{
    fmt::{Debug, Display},
    ops::{Add, AddAssign, Div, Mul, Sub, SubAssign},
};

use binrw::{binrw, BinRead, BinWrite};
use diff::{Diff, VecDiff};
use float_cmp::approx_eq;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use strum::EnumIter;

use crate::{array_of::ArrayOfPrimitive, field_of::FieldOf, FP};

/// Generic field with structure of length + data
pub type Field = ArrayOfPrimitive<u8>;

pub type U16 = FieldOf<u16>;
pub type I16 = FieldOf<i16>;
pub type U32 = FieldOf<u32>;
pub type I32 = FieldOf<i32>;
pub type F64 = FieldOf<f64>;
pub type Bool = FieldOf<u32>;

impl From<FieldOf<u32>> for bool {
    fn from(value: FieldOf<u32>) -> Self {
        value.value != 0
    }
}

impl From<bool> for FieldOf<u32> {
    fn from(value: bool) -> Self {
        Self {
            value: match value {
                true => 1,
                false => 0,
            },
        }
    }
}

#[binrw]
#[derive(Clone, Default, PartialEq)]
pub struct WString {
    // Temporary variable that holds number of elements
    #[br(temp)]
    #[bw(try_calc(u32::try_from(value.len() * core::mem::size_of::<u16>())))]
    size: u32,

    #[br(count = size / core::mem::size_of::<u16>() as u32)]
    pub value: Vec<u16>,
}

impl From<&WString> for String {
    fn from(value: &WString) -> Self {
        String::from_utf16_lossy(&value.value)
    }
}

impl From<String> for WString {
    fn from(value: String) -> Self {
        let value: Vec<u16> = value.encode_utf16().collect();
        Self { value }
    }
}

// Custom Diff implementation to only diff internal value
impl Diff for WString {
    type Repr = VecDiff<u16>;

    fn diff(&self, other: &Self) -> Self::Repr {
        self.value.diff(&other.value)
    }

    fn apply(&mut self, diff: &Self::Repr) {
        self.value.apply(diff)
    }

    fn identity() -> Self {
        Self { value: vec![] }
    }
}

impl Debug for WString {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "\"{}\"", String::from(self))
    }
}

impl Display for WString {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", String::from(self))
    }
}

#[derive(Clone, Debug, Default, Diff, PartialEq, BinRead, BinWrite)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
pub struct Rgba {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
    pub alpha: u8,
}

impl From<(u8, u8, u8)> for Rgba {
    fn from(value: (u8, u8, u8)) -> Self {
        Self {
            red: value.0,
            green: value.1,
            blue: value.2,
            alpha: 0,
        }
    }
}

impl Rgba {
    pub fn random() -> Self {
        Self {
            red: thread_rng().gen(),
            green: thread_rng().gen(),
            blue: thread_rng().gen(),
            alpha: 0,
        }
    }
}

#[derive(Copy, Clone, Debug, Default, Diff, BinRead, BinWrite, Serialize, Deserialize)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
#[serde(rename_all = "PascalCase")]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl PartialEq for Point {
    fn eq(&self, other: &Self) -> bool {
        approx_eq!(f64, self.x, other.x) && approx_eq!(f64, self.y, other.y)
    }
}

impl Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({:.FP$}, {:.FP$})", self.x, self.y)
    }
}

impl Sub for Point {
    type Output = Point;

    fn sub(self, rhs: Self) -> Self::Output {
        Point {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

impl SubAssign for Point {
    fn sub_assign(&mut self, rhs: Self) {
        self.x -= rhs.x;
        self.y -= rhs.y;
    }
}

impl Add for Point {
    type Output = Point;

    fn add(self, rhs: Self) -> Self::Output {
        Point {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl AddAssign for Point {
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
    }
}

impl Mul for Point {
    type Output = Point;

    fn mul(self, rhs: Self) -> Self::Output {
        Point {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
        }
    }
}

impl Div for Point {
    type Output = Point;

    fn div(self, rhs: Self) -> Self::Output {
        Point {
            x: self.x / rhs.x,
            y: self.y / rhs.y,
        }
    }
}

impl From<(f64, f64)> for Point {
    fn from(value: (f64, f64)) -> Self {
        Self {
            x: value.0,
            y: value.1,
        }
    }
}

impl From<f64> for Point {
    fn from(value: f64) -> Self {
        Self { x: value, y: value }
    }
}

impl Point {
    pub fn min(&self, rhs: Point) -> Point {
        Point {
            x: self.x.min(rhs.x),
            y: self.y.min(rhs.y),
        }
    }

    pub fn max(&self, rhs: Point) -> Point {
        Point {
            x: self.x.max(rhs.x),
            y: self.y.max(rhs.y),
        }
    }
}

#[derive(Clone, Debug, Diff, PartialEq, BinRead, BinWrite, strum::Display)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
#[brw(repr(u32))]
#[repr(u32)]
pub enum WobbleType {
    Spiral = 0,
    Sinusoidal = 1,
    Ellipse = 2,
    Vert8 = 3,
    Hori8 = 4,
    Unknown = 5,
}

impl Default for WobbleType {
    fn default() -> Self {
        WobbleType::Unknown
    }
}

#[derive(Clone, Debug, Diff, PartialEq, BinRead, BinWrite, strum::Display)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
#[brw(repr(u16))]
#[repr(u16)]
pub enum ObjectType {
    Unknown = 0,
    Curve = 1,
    Point = 2,
    Rectangle = 3,
    Circle = 4,
    Ellipse = 5,
    Polygon = 6,
    HatchLine = 16,
    Hatch = 32,
}
impl Default for ObjectType {
    fn default() -> Self {
        ObjectType::Unknown
    }
}

#[derive(
    Copy,
    Clone,
    Debug,
    Diff,
    PartialEq,
    strum::Display,
    Serialize_repr,
    Deserialize_repr,
    EnumIter,
    IntoPrimitive,
    TryFromPrimitive,
)]
#[diff(attr(
    #[derive(Debug, PartialEq)]
))]
#[repr(u32)]
pub enum PulseWidth {
    Ns2 = 2,
    Ns4 = 4,
    Ns6 = 6,
    Ns8 = 8,
    Ns12 = 12,
    Ns20 = 20,
    Ns30 = 30,
    Ns45 = 45,
    Ns60 = 60,
    Ns80 = 80,
    Ns100 = 100,
    Ns150 = 150,
    Ns200 = 200,
    Ns250 = 250,
    Ns350 = 350,
    Ns500 = 500,
}

impl PulseWidth {
    pub fn min_freq(&self) -> u32 {
        match self {
            PulseWidth::Ns2 => 850_000,
            PulseWidth::Ns4 => 500_000,
            PulseWidth::Ns6 => 320_000,
            PulseWidth::Ns8 => 250_000,
            PulseWidth::Ns12 => 170_000,
            PulseWidth::Ns20 => 115_000,
            PulseWidth::Ns30 => 90_000,
            PulseWidth::Ns45 => 75_000,
            PulseWidth::Ns60 => 65_000,
            PulseWidth::Ns80 => 60_000,
            PulseWidth::Ns100 => 45_000,
            PulseWidth::Ns150 => 30_000,
            PulseWidth::Ns200 => 25_000,
            PulseWidth::Ns250 => 25_000,
            PulseWidth::Ns350 => 25_000,
            PulseWidth::Ns500 => 25_000,
        }
    }

    pub fn max_freq(&self) -> u32 {
        match self {
            PulseWidth::Ns2 => 4_000_000,
            PulseWidth::Ns4 => 4_000_000,
            PulseWidth::Ns6 => 4_000_000,
            PulseWidth::Ns8 => 4_000_000,
            PulseWidth::Ns12 => 3_000_000,
            PulseWidth::Ns20 => 3_000_000,
            PulseWidth::Ns30 => 3_000_000,
            PulseWidth::Ns45 => 2_000_000,
            PulseWidth::Ns60 => 2_000_000,
            PulseWidth::Ns80 => 2_000_000,
            PulseWidth::Ns100 => 1_000_000,
            PulseWidth::Ns150 => 1_000_000,
            PulseWidth::Ns200 => 1_000_000,
            PulseWidth::Ns250 => 900_000,
            PulseWidth::Ns350 => 600_000,
            PulseWidth::Ns500 => 500_000,
        }
    }

    /// Ratio of total output power per pulse from the area under the curve of the pulse
    /// waveform over time. Pulses below 20ns are assumed to be linear discrete pulses
    /// with no decay over time, while longer pulse widths will follow natural log profile
    pub fn power_ratio(&self) -> f64 {
        match self {
            PulseWidth::Ns2 => 1.0,
            PulseWidth::Ns4 => 3.17,
            PulseWidth::Ns6 => 5.34,
            PulseWidth::Ns8 => 7.51,
            PulseWidth::Ns12 => 11.85,
            PulseWidth::Ns20 => 17.29,
            PulseWidth::Ns30 => 22.04,
            PulseWidth::Ns45 => 26.78,
            PulseWidth::Ns60 => 30.17,
            PulseWidth::Ns80 => 33.54,
            PulseWidth::Ns100 => 36.16,
            PulseWidth::Ns150 => 40.91,
            PulseWidth::Ns200 => 44.28,
            PulseWidth::Ns250 => 46.90,
            PulseWidth::Ns350 => 50.84,
            PulseWidth::Ns500 => 55.03,
        }
    }
}
