#include "game_loop.h"

#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif

namespace fn::game {

namespace {

struct LoopRT {
    LoopCfg cfg;
    Uint64 last_ticks;
    float accumulator;
    Uint64 perf_freq;
};

#if defined(__EMSCRIPTEN__)
static LoopRT g_rt;

void em_tick() {
    LoopRT& rt = g_rt;
    Uint64 now = SDL_GetPerformanceCounter();
    float frame_time = (float)((double)(now - rt.last_ticks) / (double)rt.perf_freq);
    rt.last_ticks = now;

    float cap = rt.cfg.fixed_dt * (float)rt.cfg.max_steps_per_frame;
    if (frame_time > cap) frame_time = cap;

    rt.accumulator += frame_time;
    int steps = 0;
    while (rt.accumulator >= rt.cfg.fixed_dt && steps < rt.cfg.max_steps_per_frame) {
        if (rt.cfg.on_fixed_update) rt.cfg.on_fixed_update(rt.cfg.user, rt.cfg.fixed_dt);
        rt.accumulator -= rt.cfg.fixed_dt;
        steps++;
    }

    float interp = rt.accumulator / rt.cfg.fixed_dt;
    if (rt.cfg.on_render) rt.cfg.on_render(rt.cfg.user, interp);

    if (rt.cfg.should_quit && rt.cfg.should_quit(rt.cfg.user)) {
        emscripten_cancel_main_loop();
    }
}
#endif

}  // namespace

void loop_run(SDL_Window* /*win*/, const LoopCfg& cfg) {
    if (!cfg.on_fixed_update && !cfg.on_render) return;

#if defined(__EMSCRIPTEN__)
    g_rt.cfg = cfg;
    g_rt.last_ticks = SDL_GetPerformanceCounter();
    g_rt.accumulator = 0.0f;
    g_rt.perf_freq = SDL_GetPerformanceFrequency();
    emscripten_set_main_loop(em_tick, 0, 1);
#else
    Uint64 perf_freq = SDL_GetPerformanceFrequency();
    Uint64 last = SDL_GetPerformanceCounter();
    float accumulator = 0.0f;

    for (;;) {
        if (cfg.should_quit && cfg.should_quit(cfg.user)) break;

        Uint64 now = SDL_GetPerformanceCounter();
        float frame_time = (float)((double)(now - last) / (double)perf_freq);
        last = now;

        float cap = cfg.fixed_dt * (float)cfg.max_steps_per_frame;
        if (frame_time > cap) frame_time = cap;

        accumulator += frame_time;
        int steps = 0;
        while (accumulator >= cfg.fixed_dt && steps < cfg.max_steps_per_frame) {
            if (cfg.on_fixed_update) cfg.on_fixed_update(cfg.user, cfg.fixed_dt);
            accumulator -= cfg.fixed_dt;
            steps++;
        }

        float interp = accumulator / cfg.fixed_dt;
        if (cfg.on_render) cfg.on_render(cfg.user, interp);
    }
#endif
}

}  // namespace fn::game
