use crate::ecs::generational_registry::registry_entry_by_name;
use crate::ecs::world::World;
use freecs::Entity;
use rapier3d::prelude::*;

pub fn register_entity_for_trimesh_picking(world: &mut World, entity: Entity) -> bool {
    let render_mesh = match world.get_render_mesh(entity) {
        Some(rm) => rm.clone(),
        None => return false,
    };

    let mesh = match registry_entry_by_name(&world.resources.mesh_cache.registry, &render_mesh.name)
    {
        Some(m) => m,
        None => return false,
    };

    let global_transform = match world.get_global_transform(entity) {
        Some(gt) => *gt,
        None => return false,
    };

    let vertices: Vec<Point<Real>> = mesh
        .vertices
        .iter()
        .map(|v| Point::new(v.position[0], v.position[1], v.position[2]))
        .collect();

    let indices: Vec<[u32; 3]> = mesh
        .indices
        .chunks(3)
        .filter_map(|chunk| {
            if chunk.len() == 3 {
                Some([chunk[0], chunk[1], chunk[2]])
            } else {
                None
            }
        })
        .collect();

    if vertices.is_empty() || indices.is_empty() {
        return false;
    }

    let translation = global_transform.0.column(3).xyz();
    let rotation_matrix = global_transform.0.fixed_view::<3, 3>(0, 0);
    let scale = nalgebra_glm::vec3(
        rotation_matrix.column(0).magnitude(),
        rotation_matrix.column(1).magnitude(),
        rotation_matrix.column(2).magnitude(),
    );

    let scaled_vertices: Vec<Point<Real>> = vertices
        .iter()
        .map(|v| Point::new(v.x * scale.x, v.y * scale.y, v.z * scale.z))
        .collect();

    let rotation_normalized = nalgebra::Matrix3::new(
        rotation_matrix[(0, 0)] / scale.x,
        rotation_matrix[(0, 1)] / scale.y,
        rotation_matrix[(0, 2)] / scale.z,
        rotation_matrix[(1, 0)] / scale.x,
        rotation_matrix[(1, 1)] / scale.y,
        rotation_matrix[(1, 2)] / scale.z,
        rotation_matrix[(2, 0)] / scale.x,
        rotation_matrix[(2, 1)] / scale.y,
        rotation_matrix[(2, 2)] / scale.z,
    );

    let rotation = nalgebra::UnitQuaternion::from_rotation_matrix(
        &nalgebra::Rotation3::from_matrix(&rotation_normalized),
    );

    let position = Isometry::from_parts(
        nalgebra::Translation3::new(translation.x, translation.y, translation.z),
        rotation,
    );

    world
        .resources
        .picking_world
        .add_trimesh_collider(entity, scaled_vertices, indices, position)
        .is_some()
}

pub fn unregister_entity_from_picking(world: &mut World, entity: Entity) {
    world.resources.picking_world.remove_entity(entity);
}

pub fn update_picking_transform(world: &mut World, entity: Entity) {
    let global_transform = match world.get_global_transform(entity) {
        Some(gt) => *gt,
        None => return,
    };

    let translation = global_transform.0.column(3).xyz();
    let rotation_matrix = global_transform.0.fixed_view::<3, 3>(0, 0);

    let scale = nalgebra_glm::vec3(
        rotation_matrix.column(0).magnitude(),
        rotation_matrix.column(1).magnitude(),
        rotation_matrix.column(2).magnitude(),
    );

    let rotation_normalized = nalgebra::Matrix3::new(
        rotation_matrix[(0, 0)] / scale.x,
        rotation_matrix[(0, 1)] / scale.y,
        rotation_matrix[(0, 2)] / scale.z,
        rotation_matrix[(1, 0)] / scale.x,
        rotation_matrix[(1, 1)] / scale.y,
        rotation_matrix[(1, 2)] / scale.z,
        rotation_matrix[(2, 0)] / scale.x,
        rotation_matrix[(2, 1)] / scale.y,
        rotation_matrix[(2, 2)] / scale.z,
    );

    let rotation = nalgebra::UnitQuaternion::from_rotation_matrix(
        &nalgebra::Rotation3::from_matrix(&rotation_normalized),
    );

    let position = Isometry::from_parts(
        nalgebra::Translation3::new(translation.x, translation.y, translation.z),
        rotation,
    );

    world
        .resources
        .picking_world
        .update_collider_position(entity, position);
}

pub fn register_entity_hierarchy_for_trimesh_picking(world: &mut World, root_entity: Entity) {
    let mut entities_to_register = Vec::new();
    collect_mesh_entities(world, root_entity, &mut entities_to_register);

    for entity in entities_to_register {
        register_entity_for_trimesh_picking(world, entity);
    }
}

fn collect_mesh_entities(world: &World, entity: Entity, entities: &mut Vec<Entity>) {
    if world.get_render_mesh(entity).is_some() {
        entities.push(entity);
    }

    if let Some(children) = world.resources.children_cache.get(&entity) {
        for child in children.clone() {
            collect_mesh_entities(world, child, entities);
        }
    }
}
