#ifndef PIXELDEFENSE_GAME_INPUT_H
#define PIXELDEFENSE_GAME_INPUT_H

#include <breeze.h>
#include <flecs.h>
#include <raymath.h>

#include "components.h"


typedef enum InputType {
    INPUT_NONE,
    INPUT_BUILDING,
    INPUT_SELECTED_UNITS,
    INPUT_SELECTED_OBJECT,
    INPUT_SELECTED_BUILDING,
} InputType;

#define MOUSE_BUTTON_COUNT (MOUSE_BUTTON_BACK + 1)

#define DRAG_THRESHOLD 5
#define BUTTON_COUNT (MOUSE_BUTTON_BACK + 1)

typedef struct InputMapping {
    MouseButton primaryBtn;
    MouseButton secondaryBtn;
    //MouseButton mmb;

    MouseButton backBtn;
    MouseButton moveLeft;
    MouseButton moveRight;
    MouseButton moveUp;
    MouseButton moveDown;
} InputMapping;

typedef struct InputState {
    InputType    state;
    InputMapping mapping;
    // Common
    Vector2 mouseDown;
    Vector2 mouseDownWorld;
    Vector2 mouse;
    Vector2 mouseWorld;
    f32 mouseDownElapsed[BUTTON_COUNT];
    enum {
        CURSOR_NONE,
        CURSOR_COLLECT_WOOD,
    } cursor;

    // INPUT_BUILDING
    int building;
    bool buildingCanPlace;
    Vec2i buildingPos;
    Vec2i buildingSize;
    // SELECTED_UNITS
    Rectangle pickArea;


    // SELECTED_OBJECT
    // SELECTED_BUILDING

    // Other
    struct {
        /* Selected units
         * 1: Position
         * 2: Size
         * 3: UnitSelected
         */
        ecs_query_t *selected;
    } queries;
    bool canUseMouse;
    bool canUseKeyboard;
} InputState;

static InputMapping inputDefaultMapping() {
    return (InputMapping) {
        .primaryBtn   = MOUSE_BUTTON_LEFT,
        .secondaryBtn = MOUSE_BUTTON_RIGHT,

        .backBtn   = KEY_ESCAPE,
        .moveLeft  = KEY_W,
        .moveRight = KEY_D,
        .moveUp    = KEY_UP,
        .moveDown  = KEY_S
    };
}

static void updateInputState(InputState *state, const Camera2D camera, const f32 dt) {
    for (i32 i = 0; i < BUTTON_COUNT; i++) {
        f32 *elapsed = &state->mouseDownElapsed[i];
        if (IsMouseButtonDown(i))
            *elapsed += dt;
        else if (IsMouseButtonUp(i) &&
            !IsMouseButtonReleased(i))
            *elapsed = 0;
    }

    const Vector2 mouse      = GetMousePosition();
    const Vector2 mouseWorld = GetScreenToWorld2D(mouse, camera);

    if (IsMouseButtonPressed(state->mapping.primaryBtn) ||
        IsMouseButtonPressed(state->mapping.secondaryBtn)) {
        state->mouseDown      = mouse;
        state->mouseDownWorld = mouseWorld;
    }
    state->mouse      = mouse;
    state->mouseWorld = mouseWorld;
}

static f32 inputMouseDelta(const InputState *state) {
    Vector2 start = state->mouseDown;
    Vector2 end = state->mouse;
    f32 dst = Vector2DistanceSqr(start, end);
    return dst;
}

static bool isInputBtnDragged(const InputState *state, const MouseButton btn) {
    if (!IsMouseButtonDown(btn)) return false;
    return inputMouseDelta(state) >= DRAG_THRESHOLD;
}
static bool isInputBtnJustDragged(const InputState *state, const MouseButton btn) {
    if (!IsMouseButtonReleased(btn)) return false;
    return inputMouseDelta(state) >= DRAG_THRESHOLD;
}
static bool isInputBtnJustDown(const InputState *state, const MouseButton btn) {
    return IsMouseButtonPressed(btn) && inputMouseDelta(state) < DRAG_THRESHOLD;
}
static bool isInputBtnJustUp(const InputState *state, const MouseButton btn) {
    return IsMouseButtonReleased(btn) && inputMouseDelta(state) < DRAG_THRESHOLD;
}
static bool isInputBtnDown(const InputState *state, const MouseButton btn) {
    BZ_UNUSED(state);
    return IsMouseButtonDown(btn);
}
static bool isInputBtnUp(const InputState *state, const MouseButton btn) {
    BZ_UNUSED(state);
    return IsMouseButtonUp(btn);
}

extern ECS_COMPONENT_DECLARE(InputState); // defined in main.c

#endif //PIXELDEFENSE_GAME_INPUT_H
