//! Commands for spawning and controlling cloth entities.

use crate::ecs::cloth::components::Cloth;
use crate::ecs::material::components::{Material, MaterialRef};
use crate::ecs::material::resources::material_registry_insert;
use crate::ecs::mesh::components::RenderMesh;
use crate::ecs::world::{
    BOUNDING_VOLUME, CASTS_SHADOW, CLOTH, GLOBAL_TRANSFORM, LOCAL_TRANSFORM, LOCAL_TRANSFORM_DIRTY,
    MATERIAL_REF, NAME, RENDER_MESH, World,
    components::{CastsShadow, LocalTransform, Name},
};
use freecs::Entity;
use nalgebra_glm::{Quat, Vec3};

/// Spawns a cloth entity at the given position.
///
/// The cloth hangs from the entity's origin: it spans `cloth.width` along
/// local +X centered on the origin and falls `cloth.height` down local -Y.
/// Move or rotate the entity afterwards and the pinned particles follow.
/// The entity scale must stay (1, 1, 1).
///
/// The entity is a regular mesh entity: a grid mesh named
/// `Cloth_{entity_id}` is registered in the mesh cache, a default
/// double-sided material named `Cloth_{entity_id}` is registered in the
/// material registry, and the entity casts shadows. Replace the material
/// through the registry or by setting
/// [`MaterialRef`](crate::ecs::material::components::MaterialRef) for full
/// PBR control over the sheet's appearance. Cloth materials should be
/// double-sided since both faces of the sheet are visible.
pub fn spawn_cloth(world: &mut World, cloth: Cloth, position: Vec3, name: String) -> Entity {
    let entity = crate::ecs::world::commands::spawn_entities(
        world,
        NAME | LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | CLOTH
            | RENDER_MESH
            | MATERIAL_REF
            | BOUNDING_VOLUME
            | CASTS_SHADOW,
        1,
    )[0];

    world.core.set_name(entity, Name(name));
    crate::ecs::world::commands::setup_entity_transforms(
        world,
        entity,
        LocalTransform {
            translation: position,
            rotation: Quat::identity(),
            scale: Vec3::new(1.0, 1.0, 1.0),
        },
    );

    let resource_name = format!("Cloth_{}", entity.id);
    world
        .core
        .set_render_mesh(entity, RenderMesh::new(&resource_name));
    world.core.set_casts_shadow(entity, CastsShadow);
    world
        .core
        .set_bounding_volume(entity, super::systems::cloth_bounding_volume(&cloth));

    material_registry_insert(
        &mut world.resources.assets.material_registry,
        resource_name.clone(),
        Material {
            double_sided: true,
            roughness: 0.9,
            metallic: 0.0,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .assets
        .material_registry
        .registry
        .name_to_index
        .get(&resource_name)
    {
        crate::ecs::generational_registry::registry_add_reference(
            &mut world.resources.assets.material_registry.registry,
            index,
        );
    }
    world
        .core
        .set_material_ref(entity, MaterialRef::new(resource_name));

    world.core.set_cloth(entity, cloth);
    entity
}

/// Restores a cloth to its rest pose at the entity's current transform.
///
/// Velocities are cleared and every particle returns to the flat grid. The
/// global [`Wind`](super::resources::Wind) immediately acts on the fresh
/// state.
pub fn reset_cloth(world: &mut World, entity: Entity) {
    if let Some(cloth) = world.core.get_cloth_mut(entity) {
        cloth.reset_epoch = cloth.reset_epoch.wrapping_add(1);
    }
}
