use crate::ecs::world::{INSTANCED_MESH, World};

pub fn update_instanced_mesh_caches_system(world: &mut World) {
    let entities: Vec<_> = world.query_entities(INSTANCED_MESH).collect();
    let _span = tracing::info_span!("instanced_meshes", count = entities.len()).entered();

    let mut transform_dirty_entities = Vec::new();
    let mut local_matrices_dirty_entities = Vec::new();
    let mut needs_compute_dispatch = false;
    for entity in entities {
        let parent_transform = world
            .get_global_transform(entity)
            .map(|t| t.0)
            .unwrap_or_else(nalgebra_glm::Mat4::identity);

        if let Some(instanced_mesh) = world.get_instanced_mesh_mut(entity) {
            let was_dirty = instanced_mesh.dirty;
            let had_gpu_dirty = instanced_mesh.local_matrices_gpu_dirty();
            let parent_only_changed = instanced_mesh.update_world_matrices(&parent_transform);
            instanced_mesh.take_dirty_custom_data_indices();
            if was_dirty {
                transform_dirty_entities.push(entity);
            }
            if had_gpu_dirty {
                local_matrices_dirty_entities.push(entity);
                instanced_mesh.clear_local_matrices_gpu_dirty();
            }
            if parent_only_changed {
                needs_compute_dispatch = true;
            }
        }
    }

    for entity in transform_dirty_entities {
        world
            .resources
            .mesh_render_state
            .mark_transform_dirty(entity);
    }
    for entity in local_matrices_dirty_entities {
        world
            .resources
            .mesh_render_state
            .mark_instanced_local_matrices_dirty(entity);
    }
    if needs_compute_dispatch {
        world
            .resources
            .mesh_render_state
            .mark_instanced_compute_dispatch_needed();
    }
}
