#include "engine/input.h"
#include <string.h>

static bool s_current[ACTION_COUNT];
static bool s_previous[ACTION_COUNT];

/*
 * Latched states: accumulate press/release events across frames
 * so that a press is never lost even if no update tick runs
 * during the frame it was detected.
 */
static bool s_latched_pressed[ACTION_COUNT];
static bool s_latched_released[ACTION_COUNT];

static bool s_quit_requested;

/* ── Mouse state ──────────────────────────────────── */
static int  s_mouse_x, s_mouse_y;
static bool s_mouse_current[MOUSE_BUTTON_COUNT];
static bool s_mouse_previous[MOUSE_BUTTON_COUNT];
static bool s_mouse_latched_pressed[MOUSE_BUTTON_COUNT];
static bool s_mouse_latched_released[MOUSE_BUTTON_COUNT];
static int  s_mouse_scroll;

/* ── Raw keyboard state ───────────────────────────── */
static const Uint8 *s_key_state = NULL;
static Uint8 s_latched_keys[SDL_NUM_SCANCODES];

/* Default key bindings (primary + alternate) */
static SDL_Scancode s_bindings[ACTION_COUNT] = {
    [ACTION_LEFT]  = SDL_SCANCODE_LEFT,
    [ACTION_RIGHT] = SDL_SCANCODE_RIGHT,
    [ACTION_UP]    = SDL_SCANCODE_UP,
    [ACTION_DOWN]  = SDL_SCANCODE_DOWN,
    [ACTION_JUMP]  = SDL_SCANCODE_Z,
    [ACTION_SHOOT] = SDL_SCANCODE_X,
    [ACTION_DASH]  = SDL_SCANCODE_C,
    [ACTION_PAUSE] = SDL_SCANCODE_ESCAPE,
};

/* Alternate bindings (0 = no alternate) */
static SDL_Scancode s_alt_bindings[ACTION_COUNT] = {
    [ACTION_JUMP] = SDL_SCANCODE_SPACE,
};

void input_init(void) {
    memset(s_current, 0, sizeof(s_current));
    memset(s_previous, 0, sizeof(s_previous));
    memset(s_latched_pressed, 0, sizeof(s_latched_pressed));
    memset(s_latched_released, 0, sizeof(s_latched_released));
    memset(s_mouse_current, 0, sizeof(s_mouse_current));
    memset(s_mouse_previous, 0, sizeof(s_mouse_previous));
    memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed));
    memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released));

    memset(s_latched_keys, 0, sizeof(s_latched_keys));
    s_mouse_x = s_mouse_y = 0;
    s_mouse_scroll = 0;
    s_quit_requested = false;
}

void input_poll(void) {
    /* Save previous state */
    memcpy(s_previous, s_current, sizeof(s_current));
    memcpy(s_mouse_previous, s_mouse_current, sizeof(s_mouse_current));
    s_quit_requested = false;
    s_mouse_scroll = 0;

    /* Process SDL events */
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        switch (event.type) {
        case SDL_QUIT:
            s_quit_requested = true;
            break;
        case SDL_MOUSEWHEEL:
            s_mouse_scroll += event.wheel.y;
            break;
        case SDL_KEYDOWN:
            /* Latch raw key press directly from the event.
             * More reliable than state-snapshot comparison,
             * especially on Emscripten where SDL_GetKeyboardState
             * may not reflect keys pressed within the same frame. */
            if (!event.key.repeat &&
                event.key.keysym.scancode < SDL_NUM_SCANCODES) {
                s_latched_keys[event.key.keysym.scancode] = 1;
            }
            break;
        }
    }

    /* Read keyboard state */
    s_key_state = SDL_GetKeyboardState(NULL);
    for (int i = 0; i < ACTION_COUNT; i++) {
        s_current[i] = s_key_state[s_bindings[i]];
        if (s_alt_bindings[i] && s_key_state[s_alt_bindings[i]]) {
            s_current[i] = true;
        }

        /* Latch edges: once set, stays true until consumed */
        if (s_current[i] && !s_previous[i]) {
            s_latched_pressed[i] = true;
        }
        if (!s_current[i] && s_previous[i]) {
            s_latched_released[i] = true;
        }
    }

    /* Read mouse state */
    Uint32 buttons = SDL_GetMouseState(&s_mouse_x, &s_mouse_y);

    /* Convert window coords to logical coords (SDL handles this via
       SDL_RenderSetLogicalSize, but GetMouseState returns window coords) */
    SDL_Renderer *r = SDL_GetRenderer(SDL_GetMouseFocus());
    if (r) {
        float lx, ly;
        SDL_RenderWindowToLogical(r, s_mouse_x, s_mouse_y, &lx, &ly);
        s_mouse_x = (int)lx;
        s_mouse_y = (int)ly;
    }

    s_mouse_current[MOUSE_LEFT]   = (buttons & SDL_BUTTON_LMASK) != 0;
    s_mouse_current[MOUSE_MIDDLE] = (buttons & SDL_BUTTON_MMASK) != 0;
    s_mouse_current[MOUSE_RIGHT]  = (buttons & SDL_BUTTON_RMASK) != 0;

    for (int i = 0; i < MOUSE_BUTTON_COUNT; i++) {
        if (s_mouse_current[i] && !s_mouse_previous[i]) {
            s_mouse_latched_pressed[i] = true;
        }
        if (!s_mouse_current[i] && s_mouse_previous[i]) {
            s_mouse_latched_released[i] = true;
        }
    }
}

void input_consume(void) {
    /* Clear latched states after an update tick has read them */
    memset(s_latched_pressed, 0, sizeof(s_latched_pressed));
    memset(s_latched_released, 0, sizeof(s_latched_released));
    memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed));
    memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released));
    memset(s_latched_keys, 0, sizeof(s_latched_keys));
}

bool input_pressed(Action a) {
    return s_latched_pressed[a];
}

bool input_held(Action a) {
    return s_current[a];
}

bool input_released(Action a) {
    return s_latched_released[a];
}

bool input_quit_requested(void) {
    return s_quit_requested;
}

/* ── Mouse queries ────────────────────────────────── */

void input_mouse_pos(int *x, int *y) {
    if (x) *x = s_mouse_x;
    if (y) *y = s_mouse_y;
}

bool input_mouse_pressed(MouseButton btn) {
    if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
    return s_mouse_latched_pressed[btn];
}

bool input_mouse_held(MouseButton btn) {
    if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
    return s_mouse_current[btn];
}

bool input_mouse_released(MouseButton btn) {
    if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
    return s_mouse_latched_released[btn];
}

int input_mouse_scroll(void) {
    return s_mouse_scroll;
}

/* ── Raw keyboard queries ─────────────────────────── */

bool input_key_pressed(SDL_Scancode key) {
    if (key < 0 || key >= SDL_NUM_SCANCODES) return false;
    return s_latched_keys[key] != 0;
}

bool input_key_held(SDL_Scancode key) {
    if (key < 0 || key >= SDL_NUM_SCANCODES) return false;
    return s_key_state && s_key_state[key];
}

void input_shutdown(void) {
    /* Nothing to clean up */
}
