//! Contracts and utilities for composing entities.

use std::{any::TypeId, collections::HashMap};
use super::{
    archetype::{Archetype, ArchetypeId},
    component::{Component, ComponentPool},
};

/// Represents an entity store.
///
/// Contains APIs to spawn and query entities.
#[derive(Default)]
pub struct Entities {
    archetypes: HashMap<ArchetypeId, Archetype>,
}

impl Entities {
    /// Creates an entity.
    pub fn spawn(&mut self, archetype: impl Into<Archetype>) {
        let archetype = archetype.into();
        let id = archetype.id_ref().clone();

        self.archetypes
            .entry(id)
            .or_insert(archetype);
    }

    /// Retrieves immutable archetype data based off of the components within.
    pub fn query(&self, id: impl Into<ArchetypeId>) -> Option<&ComponentPool> {
        let id = id.into();

        self.archetypes
            .get(&id)
            .map(|a| a.components_ref())
    }

    /// Retrieves mutable archetype data based of the components within.
    pub fn query_mut(&mut self, id: impl Into<ArchetypeId>) -> Option<&mut ComponentPool> {
        let id = id.into();

        self.archetypes
            .get_mut(&id)
            .map(|a| a.components_mut())
    }

    /// Grabs mutable references to multiple archetypes.
    pub fn query_disjoint_mut<const N: usize>(
        &mut self,
        ids: [impl Into<ArchetypeId>; N],
    ) -> [Option<&mut ComponentPool>; N] {
        let ids = ids.map(|i| i.into());

        self.archetypes
            .get_disjoint_mut(ids.each_ref())
            .map(|o| o.map(|a| a.components_mut()))
    }
}

/// A composition of components which represent a concrete object.
///
/// This is the primary method of interfacing with the ECS.
pub trait Entity {
    /// Converts [`self`] into an [`EntityBuilder`].
    fn into_builder(self) -> EntityBuilder;

    /// Initializes a [`QueryBuilder`] based off of the entity's type.
    fn query() -> QueryBuilder;
}

// Implements [`Entity`] for all [`Component`] implementations.
//
// A standalone component can be considered an entity.
impl<TComponent> Entity for TComponent
    where
        TComponent: Component + 'static,
{
    fn into_builder(self) -> EntityBuilder {
        let mut builder = EntityBuilder::default();
        builder.add_component(self);

        builder
    }

    fn query() -> QueryBuilder {
        let mut builder = QueryBuilder::default();
        builder.add_component::<TComponent>();

        builder
    }
}

/// Allows for the composition of an entity.
#[derive(Default)]
pub struct EntityBuilder {
    component_pool: ComponentPool,
}

impl EntityBuilder {
    /// Adds a component to the entity.
    pub fn add_component<TComponent>(&mut self, component: TComponent) -> &mut Self
        where
            TComponent: Component + 'static,
    {
        self.component_pool.push_component(component);

        self
    }

    /// Deconstructs [`self`] into a [`ComponentPool`].
    pub(crate) fn into_pool(self) -> ComponentPool {
        self.component_pool
    }
}

/// A query for a composition of components.
#[derive(Default)]
pub struct QueryBuilder {
    component_ids: Vec<TypeId>,
}

impl QueryBuilder {
    /// Adds a component to the query.
    pub fn add_component<TComponent>(&mut self) -> &mut Self
        where
            TComponent: 'static,
    {
        let id = TypeId::of::<TComponent>();

        self.component_ids.push(id);

        self
    }

    /// Deconstructs [`self`] into a span of component identifiers.
    pub(crate) fn into_ids(self) -> Vec<TypeId> {
        self.component_ids
    }
}

