use super::HierarchyNode;
use nightshade::ecs::bounding_volume::components::BoundingVolume;
use nightshade::ecs::grass::{GrassInteractor, GrassRegion};
use nightshade::ecs::navmesh::NavMeshAgent;
use nightshade::ecs::particles::components::ParticleEmitter;
use nightshade::ecs::physics::components::CharacterControllerComponent;
use nightshade::ecs::transform::components::IgnoreParentScale;
use nightshade::ecs::world::CASTS_SHADOW;
use nightshade::prelude::*;

#[derive(Clone, serde::Serialize, serde::Deserialize)]
#[serde(crate = "serde")]
pub struct EntitySnapshot {
    pub name: Option<Name>,
    pub local_transform: Option<LocalTransform>,
    #[serde(skip)]
    pub global_transform: Option<GlobalTransform>,
    #[serde(skip)]
    pub parent_entity: Option<Entity>,
    pub camera: Option<Camera>,
    pub light: Option<Light>,
    pub visibility: Option<Visibility>,
    pub render_mesh: Option<RenderMesh>,
    pub instanced_mesh: Option<InstancedMesh>,
    pub material_ref: Option<MaterialRef>,
    pub render_layer: Option<RenderLayer>,
    pub script: Option<Script>,
    pub text: Option<Text>,
    pub lines: Option<Lines>,
    pub sprite: Option<Sprite>,
    pub sprite_animator: Option<SpriteAnimator>,
    pub decal: Option<Decal>,
    pub water: Option<Water>,
    pub animation_player: Option<AnimationPlayer>,
    pub bounding_volume: Option<BoundingVolume>,
    pub casts_shadow: bool,
    pub ignore_parent_scale: bool,
    #[serde(skip)]
    pub particle_emitter: Option<ParticleEmitter>,
    #[serde(skip)]
    pub grass_region: Option<GrassRegion>,
    #[serde(skip)]
    pub grass_interactor: Option<GrassInteractor>,
    #[serde(skip)]
    pub character_controller: Option<CharacterControllerComponent>,
    #[serde(skip)]
    pub navmesh_agent: Option<NavMeshAgent>,
}

impl EntitySnapshot {
    pub fn capture(world: &World, entity: Entity) -> Self {
        let parent_entity = world.core.get_parent(entity).and_then(|p| p.0);

        Self {
            name: world.core.get_name(entity).cloned(),
            local_transform: world.core.get_local_transform(entity).cloned(),
            global_transform: world.core.get_global_transform(entity).cloned(),
            parent_entity,
            camera: world.core.get_camera(entity).cloned(),
            light: world.core.get_light(entity).cloned(),
            visibility: world.core.get_visibility(entity).cloned(),
            render_mesh: world.core.get_render_mesh(entity).cloned(),
            instanced_mesh: world.core.get_instanced_mesh(entity).cloned(),
            material_ref: world.core.get_material_ref(entity).cloned(),
            render_layer: world.core.get_render_layer(entity).cloned(),
            script: world.core.get_script(entity).cloned(),
            text: world.core.get_text(entity).cloned(),
            lines: world.core.get_lines(entity).cloned(),
            sprite: world.sprite2d.get_sprite(entity).cloned(),
            sprite_animator: world.sprite2d.get_sprite_animator(entity).cloned(),
            decal: world.core.get_decal(entity).cloned(),
            water: world.core.get_water(entity).cloned(),
            animation_player: world.core.get_animation_player(entity).cloned(),
            bounding_volume: world.core.get_bounding_volume(entity).cloned(),
            casts_shadow: world.core.entity_has_casts_shadow(entity),
            ignore_parent_scale: world.core.entity_has_ignore_parent_scale(entity),
            particle_emitter: world.core.get_particle_emitter(entity).cloned(),
            grass_region: world.core.get_grass_region(entity).cloned(),
            grass_interactor: world.core.get_grass_interactor(entity).cloned(),
            character_controller: world.core.get_character_controller(entity).cloned(),
            navmesh_agent: world.core.get_navmesh_agent(entity).cloned(),
        }
    }
}

pub fn capture_hierarchy(world: &World, entity: Entity) -> HierarchyNode {
    let snapshot = EntitySnapshot::capture(world, entity);
    let children = query_children(world, entity)
        .into_iter()
        .map(|child| capture_hierarchy(world, child))
        .collect();
    HierarchyNode {
        entity,
        snapshot,
        children,
    }
}

pub fn recreate_hierarchy_with_mapping(
    world: &mut World,
    node: &HierarchyNode,
    parent: Option<Entity>,
) -> (Entity, std::collections::HashMap<Entity, Entity>) {
    let mut mapping = std::collections::HashMap::new();
    let root_entity = recreate_hierarchy_internal_with_mapping(world, node, parent, &mut mapping);
    (root_entity, mapping)
}

fn recreate_hierarchy_internal_with_mapping(
    world: &mut World,
    node: &HierarchyNode,
    parent: Option<Entity>,
    mapping: &mut std::collections::HashMap<Entity, Entity>,
) -> Entity {
    let new_entity = recreate_entity(world, &node.snapshot, parent);
    mapping.insert(node.entity, new_entity);

    for child_node in &node.children {
        recreate_hierarchy_internal_with_mapping(world, child_node, Some(new_entity), mapping);
    }

    new_entity
}

pub fn recreate_entity(
    world: &mut World,
    snapshot: &EntitySnapshot,
    parent: Option<Entity>,
) -> Entity {
    let entities = EntityBuilder::new()
        .with_local_transform(snapshot.local_transform.unwrap_or_default())
        .with_global_transform(snapshot.global_transform.unwrap_or_default())
        .spawn(world, 1);
    let entity = entities[0];

    if let Some(name) = &snapshot.name {
        world.core.set_name(entity, name.clone());
    }

    if let Some(parent_entity) = parent
        && world.core.get_local_transform(parent_entity).is_some()
    {
        world.core.set_parent(entity, Parent(Some(parent_entity)));
    }

    if let Some(camera) = &snapshot.camera {
        world.core.set_camera(entity, *camera);
    }
    if let Some(light) = &snapshot.light {
        world.core.set_light(entity, light.clone());
    }
    if let Some(visibility) = &snapshot.visibility {
        world.core.set_visibility(entity, visibility.clone());
    }
    if let Some(render_mesh) = &snapshot.render_mesh {
        world.core.set_render_mesh(entity, render_mesh.clone());
        world.resources.mesh_render_state.mark_entity_added(entity);
        if let Some(&index) = world
            .resources
            .mesh_cache
            .registry
            .name_to_index
            .get(&render_mesh.name)
        {
            world.resources.mesh_cache.registry.add_reference(index);
        }
    }
    if let Some(material_ref) = &snapshot.material_ref {
        world.core.set_material_ref(entity, material_ref.clone());
        if let Some(&index) = world
            .resources
            .material_registry
            .registry
            .name_to_index
            .get(&material_ref.name)
        {
            world
                .resources
                .material_registry
                .registry
                .add_reference(index);
        }
    }
    if let Some(instanced_mesh) = &snapshot.instanced_mesh {
        world
            .core
            .set_instanced_mesh(entity, instanced_mesh.clone());
    }
    if let Some(render_layer) = &snapshot.render_layer {
        world.core.set_render_layer(entity, *render_layer);
    }
    if let Some(script) = &snapshot.script {
        world.core.set_script(entity, script.clone());
    }
    if let Some(text) = &snapshot.text {
        world.core.set_text(entity, text.clone());
    }
    if let Some(lines) = &snapshot.lines {
        world.core.set_lines(entity, lines.clone());
    }
    if let Some(sprite) = &snapshot.sprite {
        world.sprite2d.set_sprite(entity, sprite.clone());
    }
    if let Some(sprite_animator) = &snapshot.sprite_animator {
        world
            .sprite2d
            .set_sprite_animator(entity, sprite_animator.clone());
    }
    if let Some(decal) = &snapshot.decal {
        world.core.set_decal(entity, decal.clone());
    }
    if let Some(water) = &snapshot.water {
        world.core.set_water(entity, water.clone());
    }
    if let Some(animation_player) = &snapshot.animation_player {
        world
            .core
            .set_animation_player(entity, animation_player.clone());
    }
    if let Some(bounding_volume) = &snapshot.bounding_volume {
        world.core.set_bounding_volume(entity, *bounding_volume);
    }
    if snapshot.casts_shadow {
        world.core.add_components(entity, CASTS_SHADOW);
    }
    if snapshot.ignore_parent_scale {
        world
            .core
            .set_ignore_parent_scale(entity, IgnoreParentScale);
    }
    if let Some(particle_emitter) = &snapshot.particle_emitter {
        world
            .core
            .set_particle_emitter(entity, particle_emitter.clone());
    }
    if let Some(grass_region) = &snapshot.grass_region {
        world.core.set_grass_region(entity, grass_region.clone());
    }
    if let Some(grass_interactor) = &snapshot.grass_interactor {
        world
            .core
            .set_grass_interactor(entity, grass_interactor.clone());
    }
    if let Some(character_controller) = &snapshot.character_controller {
        world
            .core
            .set_character_controller(entity, character_controller.clone());
    }
    if let Some(navmesh_agent) = &snapshot.navmesh_agent {
        world.core.set_navmesh_agent(entity, navmesh_agent.clone());
    }

    entity
}
