use nalgebra_glm::Vec2;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SpriteBlendMode {
    Alpha,
    Additive,
    Multiply,
    Screen,
    Overlay,
    Darken,
    Lighten,
    ColorDodge,
    ColorBurn,
    Difference,
    Exclusion,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SpriteStencilMode {
    None,
    Write,
    Test,
}

impl Default for SpriteStencilMode {
    fn default() -> Self {
        Self::None
    }
}

impl Default for SpriteBlendMode {
    fn default() -> Self {
        Self::Alpha
    }
}

impl SpriteBlendMode {
    pub fn is_advanced(self) -> bool {
        matches!(
            self,
            Self::Overlay
                | Self::Darken
                | Self::Lighten
                | Self::ColorDodge
                | Self::ColorBurn
                | Self::Difference
                | Self::Exclusion
        )
    }

    pub fn advanced_blend_index(self) -> u32 {
        match self {
            Self::Overlay => 1,
            Self::Darken => 2,
            Self::Lighten => 3,
            Self::ColorDodge => 4,
            Self::ColorBurn => 5,
            Self::Difference => 6,
            Self::Exclusion => 7,
            _ => 0,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum SpriteGradient {
    None,
    Linear { angle: f32 },
    Radial { center: Vec2 },
}

impl Default for SpriteGradient {
    fn default() -> Self {
        Self::None
    }
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct NineSlice {
    pub left: f32,
    pub right: f32,
    pub top: f32,
    pub bottom: f32,
}

impl NineSlice {
    pub fn new(left: f32, right: f32, top: f32, bottom: f32) -> Self {
        Self {
            left,
            right,
            top,
            bottom,
        }
    }

    pub fn uniform(border: f32) -> Self {
        Self {
            left: border,
            right: border,
            top: border,
            bottom: border,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Sprite {
    pub position: Vec2,
    pub size: Vec2,
    pub color: [f32; 4],
    pub texture_index: u32,
    pub texture_index2: u32,
    pub blend_factor: f32,
    pub uv_min: Vec2,
    pub uv_max: Vec2,
    pub rotation: f32,
    pub depth: f32,
    pub scale: Vec2,
    pub flip_x: bool,
    pub flip_y: bool,
    pub anchor: Vec2,
    pub blend_mode: SpriteBlendMode,
    pub nine_slice: Option<NineSlice>,
    pub clip_rect: Option<[f32; 4]>,
    pub stencil_mode: SpriteStencilMode,
    pub stencil_reference: u8,
    pub gradient: SpriteGradient,
}

impl Default for Sprite {
    fn default() -> Self {
        Self {
            position: Vec2::new(0.0, 0.0),
            size: Vec2::new(100.0, 100.0),
            color: [1.0, 1.0, 1.0, 1.0],
            texture_index: 0,
            texture_index2: 0,
            blend_factor: 0.0,
            uv_min: Vec2::new(0.0, 0.0),
            uv_max: Vec2::new(1.0, 1.0),
            rotation: 0.0,
            depth: 0.0,
            scale: Vec2::new(1.0, 1.0),
            flip_x: false,
            flip_y: false,
            anchor: Vec2::new(0.0, 0.0),
            blend_mode: SpriteBlendMode::Alpha,
            nine_slice: None,
            clip_rect: None,
            stencil_mode: SpriteStencilMode::None,
            stencil_reference: 1,
            gradient: SpriteGradient::None,
        }
    }
}

impl Sprite {
    pub fn new(position: Vec2, size: Vec2) -> Self {
        Self {
            position,
            size,
            ..Default::default()
        }
    }

    pub fn with_texture(mut self, texture_index: u32) -> Self {
        self.texture_index = texture_index;
        self.texture_index2 = texture_index;
        self
    }

    pub fn with_multitexture(
        mut self,
        texture_index: u32,
        texture_index2: u32,
        blend_factor: f32,
    ) -> Self {
        self.texture_index = texture_index;
        self.texture_index2 = texture_index2;
        self.blend_factor = blend_factor;
        self
    }

    pub fn with_color(mut self, color: [f32; 4]) -> Self {
        self.color = color;
        self
    }

    pub fn with_rotation(mut self, rotation: f32) -> Self {
        self.rotation = rotation;
        self
    }

    pub fn with_scale(mut self, scale: Vec2) -> Self {
        self.scale = scale;
        self
    }

    pub fn with_depth(mut self, depth: f32) -> Self {
        self.depth = depth;
        self
    }

    pub fn with_flip_x(mut self, flip_x: bool) -> Self {
        self.flip_x = flip_x;
        self
    }

    pub fn with_flip_y(mut self, flip_y: bool) -> Self {
        self.flip_y = flip_y;
        self
    }

    pub fn with_anchor(mut self, anchor: Vec2) -> Self {
        self.anchor = anchor;
        self
    }

    pub fn with_blend_mode(mut self, blend_mode: SpriteBlendMode) -> Self {
        self.blend_mode = blend_mode;
        self
    }

    pub fn with_nine_slice(mut self, nine_slice: NineSlice) -> Self {
        self.nine_slice = Some(nine_slice);
        self
    }

    pub fn with_clip_rect(mut self, clip_rect: [f32; 4]) -> Self {
        self.clip_rect = Some(clip_rect);
        self
    }

    pub fn with_stencil_mode(mut self, stencil_mode: SpriteStencilMode) -> Self {
        self.stencil_mode = stencil_mode;
        self
    }

    pub fn with_stencil_reference(mut self, reference: u8) -> Self {
        self.stencil_reference = reference;
        self
    }

    pub fn with_gradient(mut self, gradient: SpriteGradient) -> Self {
        self.gradient = gradient;
        self
    }
}
