#include "game_world.h"

#include "behaviour_entity.h"
#include "list.h"
#include "program.h"
#include "debug.h"

static List _add_queue;
static List _remove_queue;
static List _game_entities;

static inline
size_t _internal_find_index_for_entity(void* data, const List* list) {
    for(size_t i = 0; i < _game_entities.len; ++i) {
        BehaviourEntity* entity = list_at_as(BehaviourEntity, &_game_entities, i);
        if(entity->data == data) {
            return i;
        }
    }

    return list->len;
}

static inline
void _internal_clear_removed() {
    list_foreach(BehaviourEntity*, entity, &_remove_queue) {
        size_t index = _internal_find_index_for_entity(entity->data, &_game_entities);
        entity->drop->drop(entity->data);
        list_erase(&_game_entities, index);
    }
    list_empty(&_remove_queue);
}

static inline
void _internal_process_new() {
    list_foreach(BehaviourEntity*, entity, &_add_queue) {
        list_add(&_game_entities, entity);
        entity->tc->start(entity->data);
    }
    list_empty(&_add_queue);
}

void game_world_init() {
    _game_entities = list_from_type(BehaviourEntity);
    _add_queue = list_from_type(BehaviourEntity);
    _remove_queue = list_from_type(BehaviourEntity);
}

void game_world_close() {
    _internal_clear_removed();
    _internal_process_new();
    list_foreach(BehaviourEntity*, entity, &_game_entities) {
        entity->drop->drop(entity->data);
    }
    list_empty(&_game_entities);
}

void game_world_add_entity(BehaviourEntity entity) {
    list_add(&_add_queue, &entity);
}

void game_world_destroy_entity(BehaviourEntity entity) {
    list_add(&_remove_queue, &entity);
}

void game_world_update() {
    _internal_process_new();
    list_foreach(BehaviourEntity*, entity, &_game_entities) {
        entity->tc->update(entity->data, delta_time());
    }
    _internal_clear_removed();
}

static
int _internal_compare_depth(const BehaviourEntity* a, const BehaviourEntity* b) {
    return b->tc->get_depth(b->data) - a->tc->get_depth(a->data);
}

#define AS_COMPARISON(__FN) ((int(*)(const void*, const void*))__FN)

void game_world_draw() {
    List draw_order = list_copy(&_game_entities);

    qsort(_game_entities.data, _game_entities.len, _game_entities.element_size,
        AS_COMPARISON(_internal_compare_depth));
    list_foreach(BehaviourEntity*, entity, &draw_order) {
        entity->tc->draw(entity->data);
    }
    
    list_empty(&draw_order);
}
