// Input handler routines, platform-independent.

// The Jai Input module ported to use sokol events.

#load "sokol.jai";

Event_Type :: enum u32 {  // If we set this to u8, our packing will stop matching C's.
    UNINITIALIZED :: 0;
    KEYBOARD    :: 1;
    TEXT_INPUT  :: 2;
    WINDOW      :: 3;
    MOUSE_WHEEL :: 4;
    QUIT        :: 5;
    DRAG_AND_DROP_FILES :: 6;
    TOUCH       :: 7;
}

Key_Current_State :: enum_flags u32 {
    NONE  :: 0x0;
    DOWN  :: 0x1;
    START :: 0x4;
    END   :: 0x8;
}

// We reserve 32 buttons for each gamepad.
GAMEPAD_BUTTON_COUNT :: 32;

Key_Code :: enum u32 {
    UNKNOWN :: 0;

    // Non-textual keys that have placements in the ASCII table
    // (and thus in Unicode):

    BACKSPACE   :: 8;
    TAB         :: 9;
    LINEFEED    :: 10;
    ENTER       :: 13;
    ESCAPE      :: 27;
    SPACEBAR    :: 32;

    // The letters A-Z live in here as well and may be returned
    // by keyboard events.

    DELETE      :: 127;

    ARROW_UP    :: 128;
    ARROW_DOWN  :: 129;
    ARROW_LEFT  :: 130;
    ARROW_RIGHT :: 131;

    PAGE_UP     :: 132;
    PAGE_DOWN   :: 133;

    HOME        :: 134;
    END         :: 135;

    INSERT      :: 136;

    PAUSE       :: 137;
    SCROLL_LOCK :: 138;

    ALT;
    CTRL;
    SHIFT;
    CMD;
    META :: CMD;

    F1;
    F2;
    F3;
    F4;
    F5;
    F6;
    F7;
    F8;
    F9;
    F10;
    F11;
    F12;
    F13;
    F14;
    F15;
    F16;
    F17;
    F18;
    F19;
    F20;
    F21;
    F22;
    F23;
    F24;

    PRINT_SCREEN;

    MOUSE_BUTTON_LEFT;
    MOUSE_BUTTON_MIDDLE;
    MOUSE_BUTTON_RIGHT;

    MOUSE_WHEEL_UP;
    MOUSE_WHEEL_DOWN;

    // We reserve button codes for up to 4 gamepads.
    GAMEPAD_0_BEGIN;
    GAMEPAD_0_END :: GAMEPAD_0_BEGIN + xx GAMEPAD_BUTTON_COUNT;
    GAMEPAD_1_BEGIN;
    GAMEPAD_1_END :: GAMEPAD_1_BEGIN + xx GAMEPAD_BUTTON_COUNT;
    GAMEPAD_2_BEGIN;
    GAMEPAD_2_END :: GAMEPAD_2_BEGIN + xx GAMEPAD_BUTTON_COUNT;
    GAMEPAD_3_BEGIN;
    GAMEPAD_3_END :: GAMEPAD_3_BEGIN + xx GAMEPAD_BUTTON_COUNT;

    TOUCH;
    
    // WARNING!
    //
    // We make an array whose size is controlled
    // by the last enum value in this array, so if you make
    // really big values to match Unicode code points, our
    // memory usage will become quite sorry.
    //
    //                               -jblow, 19 March 2017
    //
}

// Modifier_Flags used to use #place, but I rewrote it to use a union
// instead .... it is not necessarily clearer though! So I am switching
// it back for now...

Event :: struct {
    Modifier_Flags :: union {
        // Eventually we'd like the *_pressed modifiers to be 1 bit each,
        // but still be nameable as booleans. But for now they're 1 byte each.
        // You can compare them as a u32 using the 'packed' member.

        struct {
            // @@ This is confusing. Below key_pressed means the key was just pressed, here _pressed means the key is held down.
            shift_pressed    := false;
            ctrl_pressed     := false;
            alt_pressed      := false;
            cmd_meta_pressed := false; // Cmd on macOS, Meta on Linux
        }

        packed: u32 = 0;
    }

    type: Event_Type = Event_Type.UNINITIALIZED;

    // If keyboard event:
    key_pressed: u32;  // If not pressed, it's a key release.
    key_code := Key_Code.UNKNOWN;

    using modifier_flags: Modifier_Flags; // Only set for Event_Type.KEYBOARD.

    utf32: u32;  // If TEXT_INPUT.
    repeat := false;  // If KEYBOARD event.
    text_input_count: u16; // If KEYBOARD event that also generated TEXT_INPUT events, this will tell you how many TEXT_INPUT events after this KEYBOARD event were generated.

    typical_wheel_delta: s32;  // Used only for mouse events.
    wheel_delta: s32;          // Used only for mouse events.

    files: [..] string;  // Used only for drag and drop events. Both the array and its contents are heap-allocated, lives until events are reset for the next frame.


    touch_type: enum u8 { // Used only for touch events.
        MOVED    :: 0;
        PRESSED  :: 1;
        RELEASED :: 2;
    }
    
    touch_index:   s32;  // Used only for touch events. Index of which touch this is.
}

// Per-frame mouse deltas:
mouse_delta_x: int;
mouse_delta_y: int;
mouse_delta_z: int;

events_this_frame:   [..] Event;

input_button_states: [NUM_BUTTON_STATES] Key_Current_State;

input_application_has_focus := false;

NUM_BUTTON_STATES :: #run enum_highest_value(Key_Code) + 1;

Window_Resize_Record :: struct {
    window: Window_Type;
    width:  s32;
    height: s32;
}

Window_Move_Record :: struct {
    window: Window_Type;
    x: s32;
    y: s32;
}

get_window_resizes :: () -> [] Window_Resize_Record {
    // The return value here will stick around in memory until the next call
    // to get_window_resizes (from any thread. Actually this whole module does
    // not deal with threading, so don't do that!)

    if resizes_to_free  array_reset(*resizes_to_free);

    if !pending_resizes return .[];

    array_copy(*resizes_to_free, pending_resizes);
    this_allocation_is_not_a_leak(resizes_to_free.data);

    pending_resizes.count = 0;
    return resizes_to_free;
}

get_window_moves :: () -> [] Window_Move_Record {
    // See notes on get_window_resizes. This works the same way.
    if moves_to_free  array_reset(*moves_to_free);

    if !pending_moves return .[];

    array_copy(*moves_to_free, pending_moves);
    pending_moves.count = 0;
    return moves_to_free;
}

input_per_frame_event_and_flag_update :: () {
    // Called once per frame, probably.

    for events_this_frame {
        allocator := it.files.allocator;  // Same allocator for filenames and the array itself.
        
        for file: it.files {
            free(file,, allocator);
        }
        
        array_free(it.files,, allocator);
    }
    
    array_reset(*events_this_frame);

    mask     := ~Key_Current_State.START;
    end_mask := ~(Key_Current_State.END | .DOWN | .START);
    // @Speed: Could just keep a list of who is not currently set.
    for * input_button_states {
        if it.* & .END {
            it.* &= end_mask;
        } else {
            it.* &= mask;
        }
    }

    mouse_delta_x = 0;
    mouse_delta_y = 0;
    mouse_delta_z = 0;
}

#scope_module

pending_moves:   [..] Window_Move_Record;
moves_to_free:   [..] Window_Move_Record;

pending_resizes: [..] Window_Resize_Record;
resizes_to_free: [..] Window_Resize_Record;

#import "Basic";
#import "Window_Type";

