#include <cstdlib>
#include <dlfcn.h>
#include <sys/stat.h>
#include <cstdio>

#include "game.h"

struct GameApi {
    GameState *(*init)();
    GameState *(*init_state)();
    void (*step) (GameState *state);
    void (*cleanup) (GameState *state);
    bool (*window_should_close)();
};

struct Game {
    time_t last_write_time;
    void *gamelib_handle;
    ino_t gamelib_id;
    GameApi api;
    GameState *state;
};

time_t get_file_write_time(const char* path) {
    struct stat file_stat;
    if (stat(path, &file_stat) == 0) {
        return file_stat.st_mtime;
    }
    return 0;
}

bool load_game_api(Game* game) {
    const char* lib_path = "./game.so";
    time_t write_time = get_file_write_time(lib_path);
    
    if (write_time <= game->last_write_time) {
        return false;
    }
    
    void *gamelib_handle = dlopen(lib_path, RTLD_NOW);
    if (!gamelib_handle) {
        printf("Failed to load game.so: %s\n", dlerror());
        return false;
    }

    if (game->gamelib_handle) {
        dlclose(game->gamelib_handle);
    }
    game->gamelib_handle = gamelib_handle;
    
    game->api.init = (GameState*(*)())dlsym(game->gamelib_handle, "game_init");
    game->api.init_state = (GameState*(*)())dlsym(game->gamelib_handle, "game_init_state");
    game->api.step = (void(*)(GameState*))dlsym(game->gamelib_handle, "game_step");
    game->api.cleanup = (void(*)(GameState*))dlsym(game->gamelib_handle, "game_cleanup");
    game->api.window_should_close = (bool(*)())dlsym(game->gamelib_handle, "window_should_close");
    
    game->last_write_time = write_time;
    printf("Reloaded game.so\n");
    return true;
}

int main(void) {
    Game game = {};

    load_game_api(&game);

    game.state = game.api.init();
    while (!game.api.window_should_close()) {
        // game.frame_count++;
        // float dt = GetFrameTime();
        load_game_api(&game);

        if (game.state->should_reset) {
            delete game.state;
            game.state = game.api.init_state();
        }

        game.api.step(game.state);
    }

    game.api.cleanup(game.state);
    return 0;
}
