use std::collections::HashMap;

use crate::components::dialog::{
    DialogBubbleComponent, DialogOutcomeEvent, DialogProjectileComponent, DialogSourceComponent,
};
use crate::components::dissolve::DissolveComponent;
use crate::components::follow::FollowComponent;
use crate::components::lights::spot::SpotlightComponent;
use crate::components::player_states::{
    FallingState, IdleState, JumpingState, LeapingState, RollingState, WalkingState,
};
use crate::components::tree_instances::TreeInstancesComponent;
use crate::components::trigger::{TriggerComponent, TriggerEvent};
use crate::components::{
    CameraComponent, InputComponent, JumpComponent, MeshComponent, MovementComponent,
    PhysicsComponent, RotateComponent,
};
use crate::entity::{EntityHandle, EntityManager};
use crate::state::StateMachine;

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

pub struct Storage<T>
{
    pub components: HashMap<EntityHandle, T>,
}

impl<T> Storage<T>
{
    pub fn new() -> Self
    {
        Self {
            components: HashMap::new(),
        }
    }

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

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

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

    pub fn with<F, R>(&self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&T) -> 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 T) -> 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 World
{
    pub entities: EntityManager,
    pub transforms: Storage<Transform>,
    pub meshes: Storage<MeshComponent>,
    pub physics: Storage<PhysicsComponent>,
    pub movements: Storage<MovementComponent>,
    pub jumps: Storage<JumpComponent>,
    pub inputs: Storage<InputComponent>,
    pub player_tags: Storage<()>,
    pub state_machines: Storage<StateMachine>,
    pub idle_states: Storage<IdleState>,
    pub walking_states: Storage<WalkingState>,
    pub jumping_states: Storage<JumpingState>,
    pub falling_states: Storage<FallingState>,
    pub leaping_states: Storage<LeapingState>,
    pub rolling_states: Storage<RollingState>,
    pub cameras: Storage<CameraComponent>,
    pub spotlights: Storage<SpotlightComponent>,
    pub tree_tags: Storage<()>,
    pub dissolves: Storage<DissolveComponent>,
    pub follows: Storage<FollowComponent>,
    pub rotates: Storage<RotateComponent>,
    pub tree_instances: Storage<TreeInstancesComponent>,
    pub names: Storage<String>,
    pub triggers: Storage<TriggerComponent>,
    pub trigger_events: Vec<TriggerEvent>,
    pub dialog_sources: Storage<DialogSourceComponent>,
    pub dialog_bubbles: Storage<DialogBubbleComponent>,
    pub dialog_projectiles: Storage<DialogProjectileComponent>,
    pub bubble_tags: Storage<()>,
    pub projectile_tags: Storage<()>,
    pub dialog_outcomes: Vec<DialogOutcomeEvent>,
}

impl World
{
    pub fn new() -> Self
    {
        Self {
            entities: EntityManager::new(),
            transforms: Storage::new(),
            meshes: Storage::new(),
            physics: Storage::new(),
            movements: Storage::new(),
            jumps: Storage::new(),
            inputs: Storage::new(),
            player_tags: Storage::new(),
            state_machines: Storage::new(),
            idle_states: Storage::new(),
            walking_states: Storage::new(),
            jumping_states: Storage::new(),
            falling_states: Storage::new(),
            leaping_states: Storage::new(),
            rolling_states: Storage::new(),
            cameras: Storage::new(),
            spotlights: Storage::new(),
            tree_tags: Storage::new(),
            dissolves: Storage::new(),
            follows: Storage::new(),
            rotates: Storage::new(),
            tree_instances: Storage::new(),
            names: Storage::new(),
            triggers: Storage::new(),
            trigger_events: Vec::new(),
            dialog_sources: Storage::new(),
            dialog_bubbles: Storage::new(),
            dialog_projectiles: Storage::new(),
            bubble_tags: Storage::new(),
            projectile_tags: Storage::new(),
            dialog_outcomes: Vec::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.idle_states.remove(entity);
        self.walking_states.remove(entity);
        self.jumping_states.remove(entity);
        self.falling_states.remove(entity);
        self.leaping_states.remove(entity);
        self.rolling_states.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.tree_instances.remove(entity);
        self.names.remove(entity);
        self.triggers.remove(entity);
        self.dialog_sources.remove(entity);
        self.dialog_bubbles.remove(entity);
        self.dialog_projectiles.remove(entity);
        self.bubble_tags.remove(entity);
        self.projectile_tags.remove(entity);
        self.entities.despawn(entity);
    }

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

    pub fn active_camera_position(&self) -> glam::Vec3
    {
        self.active_camera()
            .and_then(|(entity, _)| self.transforms.get(entity))
            .map(|t| t.position)
            .unwrap_or(glam::Vec3::ZERO)
    }

    pub fn player_position(&self) -> glam::Vec3
    {
        self.player_tags
            .all()
            .first()
            .and_then(|e| self.transforms.get(*e))
            .map(|t| t.position)
            .unwrap_or(glam::Vec3::ZERO)
    }
}
