/************************************************************************************************************************
Entity module.

Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.entity;

import bubel.ecs.system;
import bubel.ecs.manager;

/************************************************************************************************************************
Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference!
*/
struct EntityID
{
    ///Index to entity in IDManager.
    uint id;
    ///Counter required for reusing ID.
    uint counter;
}

/************************************************************************************************************************
Structure of Entity. It's have only ID, but have full konwlege to get to components memory (If pointer to it is proper).
*/
struct Entity
{
    ///Entity ID.
    EntityID id;

    /************************************************************************************************************************
    Get specified component. If component doesn't exist function retun null. Pointer is valid only before next "commit()", "begin()" or "end()"
    function is called. Returned pointer shouldn't be used to store reference to entity data.
    */
    T* getComponent(T)() const
    {
        EntityManager.EntitiesBlock* block = gEM.getMetaData(&this);
        EntityManager.EntityInfo* info = block.type_info;
        if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0)
            return null;
       
        static if (EntityID.sizeof == 8)
            uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) >> 3);
        else
            uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof());
        return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof);
    }

    EntityMeta getMeta()
    {
        EntityMeta meta;
        meta.block = gEM.getMetaData(&this);
        static if (EntityID.sizeof == 8)
            meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) >> 3);
        else
            meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) / EntityID.sizeof());
        return meta;
    }
}

struct EntityMeta
{
    EntityManager.EntitiesBlock* block;
    ushort index;

    T* getComponent(T)() const
    {
        return cast(T*)(cast(void*)block + block.type_info.deltas[T.component_id] + index * T.sizeof);
    }
}

/************************************************************************************************************************
Entity template structure.

Entity contain whole information needed to create new entity. Allocating EntityTemplate is considered as more expensive operation
than adding entity. Whole components memory is stored in EntityTemplate and is copyied to newly added entity.
If you want to place several entity with small difference in data then you should take pointer to component and change it before every 
entity addition.

There is no restriction about number of allocated templates. Single template can be used from multiple threads, but if you
want to changes some components data before add entity (entity position for example) it's better to use multiple templates.
*/
export struct EntityTemplate
{
    ///Entity components data
    ubyte[] entity_data;
    ///Pointer to entity type info.
    EntityManager.EntityInfo* info;

    /************************************************************************************************************************
    Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime.
    */
    T* getComponent(T)() nothrow @nogc
    {
        if(T.component_id >= info.tmpl_deltas.length || info.tmpl_deltas[T.component_id] == ushort.max)return null;
        return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]);
    }
}
