use glam::Mat4;
use std::path::Path;

pub struct EmptyNode
{
    pub name: String,
    pub transform: Mat4,
}

impl EmptyNode
{
    pub fn new(name: String, transform: Mat4) -> Self
    {
        Self { name, transform }
    }
}

pub struct Empties
{
    nodes: Vec<EmptyNode>,
}

impl Empties
{
    fn new(nodes: Vec<EmptyNode>) -> Self
    {
        Self { nodes }
    }

    pub fn into_nodes(self) -> Vec<EmptyNode>
    {
        self.nodes
    }

    pub fn load_gltf_empties(path: impl AsRef<Path>)
        -> Result<Empties, Box<dyn std::error::Error>>
    {
        let (gltf, _buffers, _images) = gltf::import(path)?;

        let mut all_empties = Vec::new();

        for scene in gltf.scenes()
        {
            for node in scene.nodes()
            {
                Self::process_node(&node, Mat4::IDENTITY, &mut all_empties)?;
            }
        }

        Ok(Empties::new(all_empties))
    }

    fn process_node(
        node: &gltf::Node,
        parent_transform: Mat4,
        all_empties: &mut Vec<EmptyNode>,
    ) -> Result<(), Box<dyn std::error::Error>>
    {
        let local_transform = Mat4::from_cols_array_2d(&node.transform().matrix());
        let global_transform = parent_transform * local_transform;

        let is_empty = node.mesh().is_none() && node.light().is_none() && node.camera().is_none();

        if is_empty
        {
            let name = node.name().unwrap_or("Unnamed").to_string();
            all_empties.push(EmptyNode::new(name, global_transform));
        }

        for child in node.children()
        {
            Self::process_node(&child, global_transform, all_empties)?;
        }

        Ok(())
    }

    pub fn load_empties(path: impl AsRef<Path>) -> Result<Empties, Box<dyn std::error::Error>>
    {
        Self::load_gltf_empties(path)
    }

    pub fn get_empty_by_name(gltf_path: &str, name: &str) -> anyhow::Result<Option<EmptyNode>>
    {
        let empties = Self::load_empties(gltf_path)
            .map_err(|e| anyhow::anyhow!("Failed to load empty nodes: {}", e))?;

        for empty_node in empties.into_nodes()
        {
            if empty_node.name == name
            {
                return Ok(Some(empty_node));
            }
        }

        Ok(None)
    }
}
