//! Archetype-based entity storage.

use std::{any::TypeId, iter::Iterator};
use crate::{
    component::ComponentPool,
    entity::{Entity, EntityBuilder, QueryBuilder},
};

/// A unique set of components.
///
/// Typically used within an entity store to increase query performance as they
/// effectively subdivide components.
#[derive(Default)]
pub struct Archetype {
    /// Uniquely identifies this archetype by it's components.
    id: ArchetypeId,

    /// Contains all components within the archetype.
    components: ComponentPool,
}

impl Archetype {
    /// Initializes [`self`] from a [`ComponentPool`].
    pub fn from_pool(components: ComponentPool) -> Self {
        let id = ArchetypeId::from_pool(&components);

        Self { id, components }
    }

    /// Retrieves the archetypes unique identifier.
    pub fn id_ref(&self) -> &ArchetypeId {
        &self.id
    }

    /// Retrieves an immutable reference to the archetypes components.
    pub fn components_ref(&self) -> &ComponentPool {
        &self.components
    }

    /// Retrieves a mutable reference to the archetypes components.
    pub fn components_mut(&mut self) -> &mut ComponentPool {
        &mut self.components
    }
}

impl From<EntityBuilder> for Archetype {
    fn from(builder: EntityBuilder) -> Self {
        let components = builder.into_pool();

        Self::from_pool(components)
    }
}

impl<T> From<T> for Archetype
    where
        T: Entity,
{
    fn from(entity: T) -> Self {
        let builder = entity.into_builder();

        builder.into()
    }
}

/// A unique identifier for an [`Archetype`].
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct ArchetypeId {
    /// Type identifiers of components which comprise an [`Archetype`].
    component_ids: Vec<TypeId>,
}

impl ArchetypeId {
    /// Initialies [`self`] from a collection of component identifiers.
    pub fn from_ids(mut component_ids: Vec<TypeId>) -> Self {
        // Sorting to ensure type identifiers are in a known order.
        // Order effects how [`self`] is hashed.
        component_ids.sort();

        Self { component_ids }
    }

    /// Initializes [`self`] from a [`ComponentPool`].
    pub fn from_pool(components: &ComponentPool) -> Self {
        let component_ids = components
            .ids()
            .map(|i| *i)
            .collect::<Vec<_>>();

        Self::from_ids(component_ids)
    }
}

impl From<QueryBuilder> for ArchetypeId {
    fn from(builder: QueryBuilder) -> Self {
        let component_ids = builder.into_ids();

        Self::from_ids(component_ids)
    }
}

