use std::collections::HashMap;

use crate::components::dissolve::DissolveComponent;
use crate::components::follow::FollowComponent;
use crate::components::jump::JumpComponent;
use crate::components::lights::spot::SpotlightComponent;
use crate::components::{
    CameraComponent, InputComponent, MeshComponent, MovementComponent, PhysicsComponent,
    RotateComponent,
};
use crate::entity::{EntityHandle, EntityManager};
use crate::state::StateMachine;

pub use crate::utility::transform::Transform;

pub struct TransformStorage
{
    pub components: HashMap<EntityHandle, Transform>,
}

impl TransformStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: Transform)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&Transform>
    {
        self.components.get(&entity)
    }

    pub fn get_mut(&mut self, entity: EntityHandle) -> Option<&mut Transform>
    {
        self.components.get_mut(&entity)
    }

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&Transform) -> R,
    {
        self.components.get(&entity).map(f)
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut Transform) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct MeshStorage
{
    pub components: HashMap<EntityHandle, MeshComponent>,
}

impl MeshStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: MeshComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&MeshComponent>
    {
        self.components.get(&entity)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct PhysicsStorage
{
    pub components: HashMap<EntityHandle, PhysicsComponent>,
}

impl PhysicsStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: PhysicsComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<PhysicsComponent>
    {
        self.components.get(&entity).copied()
    }

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&PhysicsComponent) -> R,
    {
        self.components.get(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct MovementStorage
{
    pub components: HashMap<EntityHandle, MovementComponent>,
}

impl MovementStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: MovementComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&MovementComponent>
    {
        self.components.get(&entity)
    }

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&MovementComponent) -> R,
    {
        self.components.get(&entity).map(f)
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut MovementComponent) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct JumpStorage
{
    pub components: HashMap<EntityHandle, JumpComponent>,
}

impl JumpStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: JumpComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&JumpComponent>
    {
        self.components.get(&entity)
    }

    pub fn get_mut(&mut self, entity: EntityHandle) -> Option<&mut JumpComponent>
    {
        self.components.get_mut(&entity)
    }

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&JumpComponent) -> R,
    {
        self.components.get(&entity).map(f)
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut JumpComponent) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct InputStorage
{
    pub components: HashMap<EntityHandle, InputComponent>,
}

impl InputStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: InputComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&InputComponent>
    {
        self.components.get(&entity)
    }

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&InputComponent) -> R,
    {
        self.components.get(&entity).map(f)
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut InputComponent) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct PlayerTagStorage
{
    pub components: HashMap<EntityHandle, ()>,
}

impl PlayerTagStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle)
    {
        self.components.insert(entity, ());
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct StateMachineStorage
{
    pub components: HashMap<EntityHandle, StateMachine>,
}

impl StateMachineStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: StateMachine)
    {
        self.components.insert(entity, component);
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut StateMachine) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct CameraStorage
{
    pub components: HashMap<EntityHandle, CameraComponent>,
}

impl CameraStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: CameraComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&CameraComponent>
    {
        self.components.get(&entity)
    }

    pub fn get_mut(&mut self, entity: EntityHandle) -> Option<&mut CameraComponent>
    {
        self.components.get_mut(&entity)
    }

    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut CameraComponent) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }

    pub fn get_active(&self) -> Option<(EntityHandle, &CameraComponent)>
    {
        self.components
            .iter()
            .find(|(_, cam)| cam.is_active)
            .map(|(e, c)| (*e, c))
    }
}

pub struct SpotlightStorage
{
    pub components: HashMap<EntityHandle, SpotlightComponent>,
}

impl SpotlightStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: SpotlightComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&SpotlightComponent>
    {
        self.components.get(&entity)
    }

    pub fn get_mut(&mut self, entity: EntityHandle) -> Option<&mut SpotlightComponent>
    {
        self.components.get_mut(&entity)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct TreeTagStorage
{
    pub components: HashMap<EntityHandle, ()>,
}

impl TreeTagStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle)
    {
        self.components.insert(entity, ());
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct DissolveStorage
{
    pub components: HashMap<EntityHandle, DissolveComponent>,
}

impl DissolveStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: DissolveComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&DissolveComponent>
    {
        self.components.get(&entity)
    }

    pub fn get_mut(&mut self, entity: EntityHandle) -> Option<&mut DissolveComponent>
    {
        self.components.get_mut(&entity)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct FollowStorage
{
    pub components: HashMap<EntityHandle, FollowComponent>,
}

impl FollowStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: FollowComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&FollowComponent>
    {
        self.components.get(&entity)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct RotateStorage
{
    pub components: HashMap<EntityHandle, RotateComponent>,
}

impl RotateStorage
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

    pub fn insert(&mut self, entity: EntityHandle, component: RotateComponent)
    {
        self.components.insert(entity, component);
    }

    pub fn get(&self, entity: EntityHandle) -> Option<&RotateComponent>
    {
        self.components.get(&entity)
    }

    pub fn remove(&mut self, entity: EntityHandle)
    {
        self.components.remove(&entity);
    }

    pub fn all(&self) -> Vec<EntityHandle>
    {
        self.components.keys().copied().collect()
    }
}

pub struct World
{
    pub entities: EntityManager,
    pub transforms: TransformStorage,
    pub meshes: MeshStorage,
    pub physics: PhysicsStorage,
    pub movements: MovementStorage,
    pub jumps: JumpStorage,
    pub inputs: InputStorage,
    pub player_tags: PlayerTagStorage,
    pub state_machines: StateMachineStorage,
    pub cameras: CameraStorage,
    pub spotlights: SpotlightStorage,
    pub tree_tags: TreeTagStorage,
    pub dissolves: DissolveStorage,
    pub follows: FollowStorage,
    pub rotates: RotateStorage,
}

impl World
{
    pub fn new() -> Self
    {
        Self {
            entities: EntityManager::new(),
            transforms: TransformStorage::new(),
            meshes: MeshStorage::new(),
            physics: PhysicsStorage::new(),
            movements: MovementStorage::new(),
            jumps: JumpStorage::new(),
            inputs: InputStorage::new(),
            player_tags: PlayerTagStorage::new(),
            state_machines: StateMachineStorage::new(),
            cameras: CameraStorage::new(),
            spotlights: SpotlightStorage::new(),
            tree_tags: TreeTagStorage::new(),
            dissolves: DissolveStorage::new(),
            follows: FollowStorage::new(),
            rotates: RotateStorage::new(),
        }
    }

    pub fn spawn(&mut self) -> EntityHandle
    {
        self.entities.spawn()
    }

    pub fn despawn(&mut self, entity: EntityHandle)
    {
        self.transforms.remove(entity);
        self.meshes.remove(entity);
        self.physics.remove(entity);
        self.movements.remove(entity);
        self.jumps.remove(entity);
        self.inputs.remove(entity);
        self.player_tags.remove(entity);
        self.state_machines.remove(entity);
        self.cameras.remove(entity);
        self.spotlights.remove(entity);
        self.tree_tags.remove(entity);
        self.dissolves.remove(entity);
        self.follows.remove(entity);
        self.rotates.remove(entity);
        self.entities.despawn(entity);
    }
}
