#include "twn_game_object_c.h"
#include "twn_engine_context_c.h"
#include "twn_filewatch_c.h"
#include "twn_util_c.h"
#include "twn_util.h"
#include "twn_loop_c.h"

#include <SDL2/SDL.h>

#ifdef _WIN32
#define GAME_OBJECT_NAME "libgame.dll"
#else
#define GAME_OBJECT_NAME "libgame.so"
#endif

static void (*game_tick_callback)(void);
static void (*game_end_callback)(void);

static void *handle = NULL;


static void game_object_file_action(char const *path, enum FilewatchAction action) {
    (void)action;

    if (action == FILEWATCH_ACTION_FILE_DELETED)
        return;

    if (handle) {
        SDL_UnloadObject(handle);
        game_tick_callback = NULL;
        game_end_callback = NULL;
        handle = NULL;
    }

    handle = SDL_LoadObject(path);
    if (!handle) {
        CRY_SDL("Hot Reload Error: Cannot open game code shared object");
        return;
    }

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

    game_tick_callback = (void (*)(void))SDL_LoadFunction(handle, "game_tick");
    if (!game_tick_callback) {
        CRY("Hot Reload Error", "game_tick_callback() symbol wasn't found");
        goto ERR_GETTING_PROC;
    }

    game_end_callback = (void (*)(void))SDL_LoadFunction(handle, "game_end");
    if (!game_end_callback) {
        CRY("Hot Reload Error", "game_end_callback() symbol wasn't found");
        goto ERR_GETTING_PROC;
    }

#pragma GCC diagnostic pop

    if (fabsf(0.0f - ctx.game.frame_number) > 0.00001f) {
        log_info("Game object was reloaded\n");
        reset_state();
    }

    return;

ERR_GETTING_PROC:
    SDL_UnloadObject(handle);
    handle = NULL;
    game_tick_callback = NULL;
    game_end_callback = NULL;
}


void game_object_load(void) {
    static bool filewatch_attached;
    if (!filewatch_attached) {
        char *game_object_path;
        SDL_asprintf(&game_object_path, "%s%s", ctx.base_dir, GAME_OBJECT_NAME);
        filewatch_add_file(game_object_path, game_object_file_action);
        game_object_file_action(game_object_path, FILEWATCH_ACTION_FILE_MODIFIED);
        SDL_free(game_object_path);
        filewatch_attached = true;
    }
}


void game_object_unload(void) {
    game_end_callback();
    /* needs to be closed otherwise symbols aren't resolved again */
    SDL_UnloadObject(handle);
    game_tick_callback = NULL;
    game_end_callback = NULL;
    handle = NULL;
}


void game_object_tick(void) {
    game_tick_callback();
}
