#include "entity.h"
#include "math.h"

/* Accessed via entity_nil() */
READONLY struct entity _g_entity_nil = {
    .rel_xform = XFORM_IDENT_NOCAST,
    .world_xform = XFORM_IDENT_NOCAST,
    .sprite_quad_xform = XFORM_IDENT_NOCAST,
    .sprite_tint = COLOR_WHITE
};

/* ========================== *
 * Store allocation
 * ========================== */

struct entity_store entity_store_alloc(void)
{
    struct entity_store store = { 0 };
    store.arena = arena_alloc(GIGABYTE(64));
    return store;
}

void entity_store_release(struct entity_store *store)
{
    arena_release(&store->arena);
}

void entity_store_copy_replace(struct entity_store *dest, struct entity_store *src)
{
    struct arena orig_arena = dest->arena;
    MEMCPY_STRUCT(dest, src);
    dest->arena = orig_arena;
    arena_copy_replace(&dest->arena, &src->arena);
}

/* ========================== *
 * Allocation
 * ========================== */

struct entity *entity_alloc(struct entity_store *store)
{
    struct entity *entity = NULL;
    if (store->first_free.gen) {
        /* Reuse from free list */
        entity = entity_from_handle(store, store->first_free);
        store->first_free = entity->next_free;
        entity->next_free = (struct entity_handle) { 0 };
    } else {
        /* Make new */
        u64 idx = store->count++;
        entity = arena_push(&store->arena, struct entity);
        *entity = *entity_nil();
        entity->handle = (struct entity_handle) { .gen = 1, .idx = idx };
    }
    return entity;
}

void entity_release(struct entity_store *store, struct entity *entity)
{
    struct entity_handle handle = (struct entity_handle) { .gen = entity->handle.gen, .idx = entity->handle.idx };
    *entity = *entity_nil();
    entity->handle = handle;
    entity->next_free = store->first_free;
    store->first_free = handle;
}

/* ========================== *
 * Query
 * ========================== */

struct entity_array entity_store_as_array(struct entity_store *store)
{
    return (struct entity_array) {
        .entities = (struct entity *)store->arena.base,
        .count = store->count
    };
}

/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */
struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle)
{
    if (handle.idx < store->count) {
        struct entity *entities = (struct entity *)store->arena.base;
        struct entity *entity = &entities[handle.idx];
        if (entity->handle.gen == handle.gen) {
            return entity;
        }
    }
    return entity_nil();
}

struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop)
{
    struct entity_array entities_array = entity_store_as_array(store);
    for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
        struct entity *ent = &entities_array.entities[entity_index];
        if (ent->valid && entity_has_prop(ent, prop)) {
            return ent;
        }
    }
    return entity_nil();
}

struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props)
{
    struct entity_array entities_array = entity_store_as_array(store);
    for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
        struct entity *ent = &entities_array.entities[entity_index];
        if (ent->valid) {
            b32 all = true;
            for (u64 i = 0; i < props.count; ++i) {
                if (!entity_has_prop(ent, props.props[i])) {
                    all = false;
                    break;
                }
            }
            if (all) {
                return ent;
            }
        }
    }
    return entity_nil();
}

/* ========================== *
 * Tree
 * ========================== */

void entity_link(struct entity_store *store, struct entity *parent, struct entity *child)
{
    struct entity *first_child = entity_from_handle(store, parent->first);
    struct entity *last_child = entity_from_handle(store, parent->last);

    child->prev = last_child->handle;
    child->parent = parent->handle;

    if (last_child->valid) {
        last_child->next = child->handle;
    }
    parent->last = child->handle;

    if (!first_child->valid) {
        parent->first = child->handle;
    }
}
