#include "engine/core.h"
#include "engine/input.h"
#include "engine/renderer.h"
#include "engine/audio.h"
#include "engine/assets.h"
#include <stdio.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

Engine g_engine = {0};

static GameCallbacks s_callbacks = {0};

void engine_set_callbacks(GameCallbacks cb) {
    s_callbacks = cb;
}

bool engine_init(void) {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) != 0) {
        fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
        return false;
    }

    int win_w = SCREEN_WIDTH * WINDOW_SCALE;
    int win_h = SCREEN_HEIGHT * WINDOW_SCALE;

    g_engine.window = SDL_CreateWindow(
        WINDOW_TITLE,
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        win_w, win_h,
        SDL_WINDOW_SHOWN
    );
    if (!g_engine.window) {
        fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
        return false;
    }

    g_engine.renderer = SDL_CreateRenderer(
        g_engine.window, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
    );
    if (!g_engine.renderer) {
        fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError());
        return false;
    }

    /* Set logical size for pixel-perfect scaling */
    SDL_RenderSetLogicalSize(g_engine.renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* nearest neighbor */

    /* Initialize subsystems */
    input_init();
    renderer_init(g_engine.renderer);
    audio_init();
    assets_init(g_engine.renderer);

    g_engine.running = true;
    g_engine.tick = 0;
    g_engine.fps = 0.0f;

    printf("Engine initialized: %dx%d (x%d scale)\n",
           SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_SCALE);

    return true;
}

/* ── Frame-loop state (file-scope so engine_frame can access it) ── */
static uint64_t s_freq;
static uint64_t s_prev_time;
static float    s_accumulator;
static float    s_fps_timer;
static int      s_fps_frames;

/*
 * engine_frame -- execute exactly one iteration of the game loop.
 *
 * Called repeatedly by the platform layer:
 *   Native  : tight while-loop in engine_run()
 *   Browser : emscripten_set_main_loop() via requestAnimationFrame
 */
static void engine_frame(void) {
    uint64_t current_time = SDL_GetPerformanceCounter();
    float frame_time = (float)(current_time - s_prev_time) / (float)s_freq;
    s_prev_time = current_time;

    /* Clamp frame time to avoid spiral of death */
    if (frame_time > MAX_FRAME_SKIP * DT) {
        frame_time = MAX_FRAME_SKIP * DT;
    }

    /* FPS counter */
    s_fps_timer += frame_time;
    s_fps_frames++;
    if (s_fps_timer >= 1.0f) {
        g_engine.fps = (float)s_fps_frames / s_fps_timer;
        s_fps_timer = 0.0f;
        s_fps_frames = 0;
    }

    /* ── Input ────────────────────────────── */
    input_poll();
    if (input_quit_requested()) {
        g_engine.running = false;
#ifdef __EMSCRIPTEN__
        if (s_callbacks.shutdown) {
            s_callbacks.shutdown();
        }
        emscripten_cancel_main_loop();
#endif
        return;
    }

    /* ── Fixed timestep update ────────────── */
    s_accumulator += frame_time;
    while (s_accumulator >= DT) {
        if (s_callbacks.update) {
            s_callbacks.update(DT);
        }
        input_consume();
        g_engine.tick++;
        s_accumulator -= DT;
    }

    /* ── Render ───────────────────────────── */
    g_engine.interpolation = s_accumulator / DT;
    renderer_clear();
    if (s_callbacks.render) {
        s_callbacks.render(g_engine.interpolation);
    }
    renderer_present();
}

void engine_run(void) {
    if (s_callbacks.init) {
        s_callbacks.init();
    }

    /* Initialise frame-loop timing state */
    s_freq       = SDL_GetPerformanceFrequency();
    s_prev_time  = SDL_GetPerformanceCounter();
    s_accumulator = 0.0f;
    s_fps_timer  = 0.0f;
    s_fps_frames = 0;

#ifdef __EMSCRIPTEN__
    /*
     * Hand control to the browser's requestAnimationFrame loop.
     * fps=0  : match display refresh rate
     * simulate_infinite_loop=1 : don't return from this function
     *   (so the rest of engine_run never executes until shutdown)
     */
    emscripten_set_main_loop(engine_frame, 0, 1);
#else
    while (g_engine.running) {
        engine_frame();
    }
#endif

    if (s_callbacks.shutdown) {
        s_callbacks.shutdown();
    }
}

void engine_shutdown(void) {
    assets_free_all();
    audio_shutdown();
    renderer_shutdown();
    input_shutdown();

    if (g_engine.renderer) SDL_DestroyRenderer(g_engine.renderer);
    if (g_engine.window)   SDL_DestroyWindow(g_engine.window);
    SDL_Quit();

    printf("Engine shut down cleanly.\n");
}
