use super::snapshot::recreate_hierarchy_with_mapping;
use super::{UndoResult, UndoableOperation, entity_exists};
use nightshade::prelude::*;

pub trait Reversible {
    type Context;
    type Result;
    fn reverse(&self, context: &mut Self::Context) -> (Self, Self::Result)
    where
        Self: Sized;
}

impl Reversible for UndoableOperation {
    type Context = World;
    type Result = UndoResult;

    fn reverse(&self, context: &mut Self::Context) -> (Self, Self::Result)
    where
        Self: Sized,
    {
        match self {
            UndoableOperation::EntityCreated {
                hierarchy,
                current_entity,
            } => {
                despawn_recursive_immediate(context, *current_entity);
                (
                    UndoableOperation::EntityDeleted {
                        hierarchy: hierarchy.clone(),
                        deleted_entity: *current_entity,
                    },
                    UndoResult {
                        select_entity: None,
                        entity_mapping: None,
                    },
                )
            }
            UndoableOperation::EntityDeleted { hierarchy, .. } => {
                let original_parent = hierarchy
                    .snapshot
                    .parent_entity
                    .filter(|&parent| entity_exists(context, parent));
                let (new_entity, id_mapping) =
                    recreate_hierarchy_with_mapping(context, hierarchy, original_parent);
                context.resources.children_cache_valid = false;

                (
                    UndoableOperation::EntityCreated {
                        hierarchy: hierarchy.clone(),
                        current_entity: new_entity,
                    },
                    UndoResult {
                        select_entity: Some(new_entity),
                        entity_mapping: Some(id_mapping),
                    },
                )
            }
            UndoableOperation::TransformChanged {
                entity,
                old_transform,
                new_transform,
            } => {
                let exists = entity_exists(context, *entity);
                if exists {
                    context.core.set_local_transform(*entity, *old_transform);
                    mark_local_transform_dirty(context, *entity);
                }
                (
                    UndoableOperation::TransformChanged {
                        entity: *entity,
                        old_transform: *new_transform,
                        new_transform: *old_transform,
                    },
                    UndoResult {
                        select_entity: if exists { Some(*entity) } else { None },
                        entity_mapping: None,
                    },
                )
            }
            UndoableOperation::BulkTransformChanged { transforms } => {
                let mut first_entity = None;
                let reversed_transforms: Vec<_> = transforms
                    .iter()
                    .map(|(entity, old_transform, new_transform)| {
                        let exists = entity_exists(context, *entity);
                        if exists {
                            context.core.set_local_transform(*entity, *old_transform);
                            mark_local_transform_dirty(context, *entity);
                            if first_entity.is_none() {
                                first_entity = Some(*entity);
                            }
                        }
                        (*entity, *new_transform, *old_transform)
                    })
                    .collect();
                (
                    UndoableOperation::BulkTransformChanged {
                        transforms: reversed_transforms,
                    },
                    UndoResult {
                        select_entity: first_entity,
                        entity_mapping: None,
                    },
                )
            }
            UndoableOperation::BulkEntitiesCreated {
                hierarchies,
                entities,
            } => {
                for entity in entities {
                    despawn_recursive_immediate(context, *entity);
                }
                (
                    UndoableOperation::BulkEntitiesDeleted {
                        hierarchies: hierarchies.clone(),
                        deleted_entities: entities.clone(),
                    },
                    UndoResult {
                        select_entity: None,
                        entity_mapping: None,
                    },
                )
            }
            UndoableOperation::BulkEntitiesDeleted {
                hierarchies,
                deleted_entities: _,
            } => {
                let mut new_entities = Vec::new();
                let mut all_mappings = std::collections::HashMap::new();

                for hierarchy in hierarchies {
                    let original_parent = hierarchy
                        .snapshot
                        .parent_entity
                        .filter(|&parent| entity_exists(context, parent));
                    let (new_entity, id_mapping) =
                        recreate_hierarchy_with_mapping(context, hierarchy, original_parent);
                    new_entities.push(new_entity);
                    all_mappings.extend(id_mapping);
                }

                context.resources.children_cache_valid = false;

                let first_entity = new_entities.first().copied();

                (
                    UndoableOperation::BulkEntitiesCreated {
                        hierarchies: hierarchies.clone(),
                        entities: new_entities,
                    },
                    UndoResult {
                        select_entity: first_entity,
                        entity_mapping: Some(all_mappings),
                    },
                )
            }
        }
    }
}
