use std::{fmt::Debug, rc::Rc};

use glam::Vec2;
use image::{DynamicImage, GenericImageView};

use crate::{
    handle::Handle,
    prelude::Transform,
    renderer::traits::Shader,
    sprite::Sprite,
    types::{DrawContent, Rect},
};

#[derive(Debug)]
pub enum Command {
    // Draw commands only
    Draw(Draw),
    DrawUi(Draw),
}

#[allow(clippy::enum_variant_names)]
#[derive(Debug)]
pub enum ResourceCommand {
    CreateTexture(CreateTexture),
    CreateRawTexture(CreateRawTexture),
    RemoveTexture(RemoveTexture),
}

pub struct CreateRawTexture {
    pub handle: Handle,
    pub texture: wgpu::Texture,
}

impl Debug for CreateRawTexture {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CreateRawTextureCommand")
            .field("data", &self.texture.size())
            .finish()
    }
}

pub struct CreateTexture {
    pub handle: Handle,
    pub data: DynamicImage,
}

impl Debug for CreateTexture {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CreateTextureCommand")
            .field("data", &self.data.dimensions())
            .finish()
    }
}

#[derive(Debug)]
pub struct RemoveTexture(pub u64);

pub struct Draw {
    pub shader: Rc<dyn Shader>,
    pub shader_data: Option<Vec<u8>>,
    pub(crate) content: DrawContent,
    pub src: Option<Rect>,
    pub transform: Transform,
    // quad size
    pub size: Vec2,
    pub flip_x: bool,
    pub flip_y: bool,
    pub anchor: Vec2,
    pub transparent: bool,
}

impl Debug for Draw {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("DrawCommand")
            .field("shader", &self.shader.key())
            .field("content", &self.content)
            .field("src", &self.src)
            .field("transform", &self.transform)
            .field("flip_x", &self.flip_x)
            .field("flip_y", &self.flip_y)
            .field("anchor", &self.anchor)
            .field("size", &self.size)
            .field("transparent", &self.transparent)
            .finish()
    }
}

impl Draw {
    pub fn new(
        sprite: &Sprite,
        transform: Transform,
        renderer: Rc<dyn Shader>,
        renderer_data: Option<Vec<u8>>,
    ) -> Self {
        // Base size derives from sprite (or its src rect)
        let base_size = if let Some(src) = &sprite.src {
            src.max - src.min
        } else {
            sprite.size.as_vec2()
        };
        Draw {
            shader: renderer,
            shader_data: renderer_data,
            content: DrawContent::Texture {
                handle: sprite.texture.clone(),
                color: sprite.color,
            },
            src: sprite.src.clone(),
            flip_x: sprite.flip_x,
            flip_y: sprite.flip_y,
            anchor: sprite.anchor,
            transform,
            size: base_size,
            transparent: sprite.color.is_transparent(),
        }
    }
}
