#include "game_world.h"

#include <stdlib.h>
#include <assert.h>
#include <stdio.h>

#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
#define MIN_LEVEL_HEIGHT 8

Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height) {
    assert(level_width >= MIN_LEVEL_WIDTH); // We should probably have failed earlier.
    assert(level_height >= MIN_LEVEL_HEIGHT);

    Game_World *w = (Game_World *)calloc(1, sizeof(Game_World));
    assert(w != NULL);
    w->seed = seed;

    w->max_entities = level_width * level_height;
    w->entities = (Entity *)calloc(w->max_entities, sizeof(Entity));

    // Set up entity-id queue.
    assert(squeue_init(&w->entity_id_queue, w->max_entities, sizeof(Entity_ID)));
    for(uint16_t i = 0; i < w->entity_id_queue.capacity; i++) {
        Entity_ID id = (Entity_ID)i;
        assert(squeue_push(&w->entity_id_queue, &id));
    }

    grid_initialize(&w->grid, level_width, level_height);

    { // TEMP: SS - Testing ..
        Entity_ID entity_food;
        assert(game_world_create_entity(
            w,
            Entity_Type_Food,
            4, 8,
            &entity_food
        ));
    }
    
    return w;
}

void game_world_destroy(Game_World *world) {
    assert(world != NULL);
    
    world->seed = 0;
    world->max_entities = 0;
    free(world->entities);
    grid_dispose(&world->grid);
    squeue_free(&world->entity_id_queue);
    
    free(world);
}

bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, uint16_t y, Entity_ID *out_entity_id) {
    assert(world != NULL);

    *out_entity_id = INVALID_ENTITY_ID;

    if(x >= world->grid.width) {
        printf("Failed to create entity; Invalid x-coordinate.\n");
        return false;
    }
    if(y >= world->grid.height) {
        printf("Failed to create entity; Invalid y-coordinate.\n");
        return false;
    }
    
    Entity_ID id = 0;
    if(!squeue_pop(&world->entity_id_queue, (void *)&id)) {
        printf("No free entity ids.\n");
        return false;
    }
    
    world->entities[id] = (Entity) {
        .active = true,
        .type = type,
        .child = INVALID_ENTITY_ID
    };

    Grid_Cell *cell = grid_get_cell(&world->grid, x, y); // TEMP: SS - Hardcoded coordinates. // TODO: SS - Find good coordinate.
    assert(cell != NULL);
    assert(grid_try_add_entity_to_cell(cell, &world->entities[id]));

    *out_entity_id = id;
    return true;
}

void game_world_destroy_entity(Game_World *world, Entity_ID entity_id) {
    assert(world != NULL);

    Entity *entity = game_world_try_get_entity_by_id(
        world,
        entity_id
    );
    assert(entity != NULL);
    entity->active = false;

    Grid_Cell *cell = grid_get_cell(&world->grid, entity->x, entity->y);
    assert(cell != NULL);
    assert(grid_try_remove_entity_from_cell(cell));

    squeue_push(&world->entity_id_queue, (void *)&entity_id);
}

Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id) {
    assert(world != NULL);

    if(id >= world->max_entities) {
        return NULL;
    }

    return &world->entities[id];
}