use serde::{Deserialize, Serialize};

use crate::ecs::mesh::components::Mesh;
use crate::ecs::transform::LocalTransform;

use super::asset_uuid::AssetUuid;
use super::material::SceneMaterial;

/// Mesh data embedded directly in a scene file. Keeps the original mesh name
/// so it can be reinserted into the runtime mesh cache under the name that
/// entity `RenderMesh` components reference.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddedMesh {
    pub name: String,
    pub mesh: Mesh,
}

impl EmbeddedMesh {
    pub fn new(name: impl Into<String>, mesh: Mesh) -> Self {
        Self {
            name: name.into(),
            mesh,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneInstancedMesh {
    #[serde(default)]
    pub mesh_uuid: Option<AssetUuid>,
    #[serde(default)]
    pub mesh_name: Option<String>,
    pub instances: Vec<SceneMeshInstance>,
    #[serde(default)]
    pub material: Option<SceneMaterial>,
}

impl SceneInstancedMesh {
    pub fn from_name(name: impl Into<String>, instances: Vec<SceneMeshInstance>) -> Self {
        Self {
            mesh_uuid: None,
            mesh_name: Some(name.into()),
            instances,
            material: None,
        }
    }

    pub fn from_uuid(uuid: AssetUuid, instances: Vec<SceneMeshInstance>) -> Self {
        Self {
            mesh_uuid: Some(uuid),
            mesh_name: None,
            instances,
            material: None,
        }
    }

    pub fn with_material(mut self, material: SceneMaterial) -> Self {
        self.material = Some(material);
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneMeshInstance {
    pub translation: [f32; 3],
    pub rotation: [f32; 4],
    pub scale: [f32; 3],
    #[serde(default)]
    pub color: Option<[f32; 4]>,
}

impl SceneMeshInstance {
    pub fn new(translation: [f32; 3]) -> Self {
        Self {
            translation,
            rotation: [0.0, 0.0, 0.0, 1.0],
            scale: [1.0, 1.0, 1.0],
            color: None,
        }
    }

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

    pub fn with_scale(mut self, scale: [f32; 3]) -> Self {
        self.scale = scale;
        self
    }

    pub fn with_uniform_scale(mut self, scale: f32) -> Self {
        self.scale = [scale, scale, scale];
        self
    }

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

    pub fn to_local_transform(&self) -> LocalTransform {
        LocalTransform {
            translation: nalgebra_glm::Vec3::new(
                self.translation[0],
                self.translation[1],
                self.translation[2],
            ),
            rotation: nalgebra_glm::Quat::new(
                self.rotation[3],
                self.rotation[0],
                self.rotation[1],
                self.rotation[2],
            ),
            scale: nalgebra_glm::Vec3::new(self.scale[0], self.scale[1], self.scale[2]),
        }
    }
}

impl From<LocalTransform> for SceneMeshInstance {
    fn from(transform: LocalTransform) -> Self {
        Self {
            translation: [
                transform.translation.x,
                transform.translation.y,
                transform.translation.z,
            ],
            rotation: [
                transform.rotation.i,
                transform.rotation.j,
                transform.rotation.k,
                transform.rotation.w,
            ],
            scale: [transform.scale.x, transform.scale.y, transform.scale.z],
            color: None,
        }
    }
}
