#include "twn_game_api.h"

#include "world.h"

#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <tgmath.h>


static void update_tiles(struct World *world) {
    for (size_t row = 0; row < world->tilemap_height; ++row) {
        for (size_t col = 0; col < world->tilemap_width; ++col) {
            world->tiles[(row * world->tilemap_width) + col] = (struct Tile) {
                .rect = (Rect) {
                    .x = (float)(col * world->tile_size),
                    .y = (float)(row * world->tile_size),
                    .w = (float)world->tile_size,
                    .h = (float)world->tile_size,
                },
                .type = world->tilemap[row][col],
            };
        }
    }
}


static Vec2 to_grid_location(struct World *world, float x, float y) {
    return (Vec2) {
        .x = floor(x / (float)world->tile_size),
        .y = floor(y / (float)world->tile_size),
    };
}


static void drawdef_debug(struct World *world) {
    if (!ctx.debug) return;

    for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
        if (world->tiles[i].type == TILE_TYPE_VOID) continue;

        draw_rectangle(world->tiles[i].rect, (Color) { 255, 0, 255, 128 });
    }
}


struct World *world_create(void) {
    struct World *world = malloc(sizeof *world);

    *world = (struct World) {
        .tiles = NULL,
        .tile_size = 42,
        .tilemap_width = 20,
        .tilemap_height = 12,
        .gravity = 1,
    };

    /* create the tilemap */
    /* it simply stores what's in each tile as a 2d array */
    /* on its own, it's entirely unrelated to drawing or logic */
    world->tilemap = malloc(sizeof *world->tilemap * world->tilemap_height);
    for (size_t i = 0; i < world->tilemap_height; ++i) {
        world->tilemap[i] = malloc(sizeof **world->tilemap * world->tilemap_width);

        for (size_t j = 0; j < world->tilemap_width; ++j) {
            world->tilemap[i][j] = TILE_TYPE_VOID;
        }
    }

    for (size_t i = 0; i < 12; ++i) {
        world->tilemap[world->tilemap_height-2][2+i] = TILE_TYPE_SOLID;
    }
    world->tilemap[world->tilemap_height-3][8] = TILE_TYPE_SOLID;
    world->tilemap[world->tilemap_height-4][8] = TILE_TYPE_SOLID;
    world->tilemap[world->tilemap_height-5][10] = TILE_TYPE_SOLID;
    world->tilemap[world->tilemap_height-6][10] = TILE_TYPE_SOLID;
    for (size_t i = 0; i < 7; ++i) {
        world->tilemap[world->tilemap_height-6][12+i] = TILE_TYPE_SOLID;
    }

    /* the tiles array contains data meant to be used by other logic */
    /* most importantly, it is used to draw the tiles */
    const size_t tile_count = world->tilemap_height * world->tilemap_width;
    world->tiles = malloc(sizeof *world->tiles * tile_count);
    update_tiles(world);

    return world;
}


void world_destroy(struct World *world) {
    free(world->tiles);

    for (size_t i = 0; i < world->tilemap_height; ++i) {
        free(world->tilemap[i]);
    }
    free(world->tilemap);

    free(world);
}


void world_drawdef(struct World *world) {
    for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
        if (world->tiles[i].type == TILE_TYPE_VOID)
            continue;

        m_sprite("/assets/white.png", world->tiles[i].rect);
    }

    drawdef_debug(world);
}


bool world_find_rect_intersects(struct World *world, Rect rect, Rect *intersection) {
    bool is_intersecting = false;

    const size_t tile_count = world->tilemap_height * world->tilemap_width;
    for (size_t i = 0; i < tile_count; ++i) {
        if (world->tiles[i].type == TILE_TYPE_VOID)
            continue;

        Rect const tile_frect = world->tiles[i].rect;

        is_intersecting = rect_intersects(rect, tile_frect);

        if (intersection)
            *intersection = rect_overlap(rect, tile_frect);

        if (is_intersecting)
            break; 
    }

    return is_intersecting;
}


bool world_is_tile_at(struct World *world, float x, float y) {
    Vec2 position_in_grid = to_grid_location(world, x, y);
    return world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] != TILE_TYPE_VOID;
}


void world_place_tile(struct World *world, float x, float y) {
    Vec2 position_in_grid = to_grid_location(world, x, y);
    world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] = TILE_TYPE_SOLID;
    update_tiles(world);
}


void world_remove_tile(struct World *world, float x, float y) {
    Vec2 position_in_grid = to_grid_location(world, x, y);
    world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] = TILE_TYPE_VOID;
    update_tiles(world);
}