#ifndef _ENGINE_INPUT_H_
#define _ENGINE_INPUT_H_

#define MAX_INPUT_FRONTEND_SETS 8

enum InputSource {
  Keyboard_Apostrophe,
  Keyboard_Comma,
  Keyboard_Minus,
  Keyboard_Period,
  Keyboard_Slash,
  Keyboard_0,
  Keyboard_1,
  Keyboard_2,
  Keyboard_3,
  Keyboard_4,
  Keyboard_5,
  Keyboard_6,
  Keyboard_7,
  Keyboard_8,
  Keyboard_9,
  Keyboard_Semicolon,
  Keyboard_Equal,
  Keyboard_A,
  Keyboard_B,
  Keyboard_C,
  Keyboard_D,
  Keyboard_E,
  Keyboard_F,
  Keyboard_G,
  Keyboard_H,
  Keyboard_I,
  Keyboard_J,
  Keyboard_K,
  Keyboard_L,
  Keyboard_M,
  Keyboard_N,
  Keyboard_O,
  Keyboard_P,
  Keyboard_Q,
  Keyboard_R,
  Keyboard_S,
  Keyboard_T,
  Keyboard_U,
  Keyboard_V,
  Keyboard_W,
  Keyboard_X,
  Keyboard_Y,
  Keyboard_Z,
  Keyboard_Space,
  Keyboard_Escape,
  Keyboard_Enter,
  Keyboard_Tab,
  Keyboard_Backspace,
  Keyboard_Insert,
  Keyboard_Delete,
  Keyboard_Right,
  Keyboard_Left,
  Keyboard_Down,
  Keyboard_Up,
  Keyboard_PageUp,
  Keyboard_PageDown,
  Keyboard_Home,
  Keyboard_End,
  Keyboard_CapsLock,
  Keyboard_ScrollLock,
  Keyboard_NumLock,
  Keyboard_PrintScreen,
  Keyboard_Pause,
  Keyboard_F1,
  Keyboard_F2,
  Keyboard_F3,
  Keyboard_F4,
  Keyboard_F5,
  Keyboard_F6,
  Keyboard_F7,
  Keyboard_F8,
  Keyboard_F9,
  Keyboard_F10,
  Keyboard_F11,
  Keyboard_F12,
  Keyboard_LeftShift,
  Keyboard_LeftControl,
  Keyboard_LeftAlt,
  Keyboard_LeftMeta,
  Keyboard_RightShift,
  Keyboard_RightControl,
  Keyboard_RightAlt,
  Keyboard_RightMeta,
  Keyboard_Menu,
  Keyboard_LeftBracket,
  Keyboard_Backslash,
  Keyboard_RightBracket,
  Keyboard_Grave,
  Keyboard_Pad_0,
  Keyboard_Pad_1,
  Keyboard_Pad_2,
  Keyboard_Pad_3,
  Keyboard_Pad_4,
  Keyboard_Pad_5,
  Keyboard_Pad_6,
  Keyboard_Pad_7,
  Keyboard_Pad_8,
  Keyboard_Pad_9,
  Keyboard_Pad_Period,
  Keyboard_Pad_Divide,
  Keyboard_Pad_Multiply,
  Keyboard_Pad_Minus,
  Keyboard_Pad_Add,
  Keyboard_Pad_Enter,
  Keyboard_Pad_Equal,

  Mouse_Left_Button,
  Mouse_Right_Button,
  Mouse_Position_X,
  Mouse_Position_Y,

  InputSource_COUNT
};

struct InputBackendPair {
  enum InputSource source;
  uint32_t binding;
};

enum InputSignalType {
    InputSignal_Pass
  , InputSignal_Up
  , InputSignal_Down
  , InputSignal_Direction
  , InputSignal_Point
  , InputSignal_ViewportPoint
};

struct InputSignal {
  enum InputSignalType type;

  // inputs
  uint32_t bindings[2];
  union {
    Entity click_camera;
  };

  // output
  union {
    bool boolean;
    alias_pga2d_Direction direction;
    alias_pga2d_Point point;
  };
  union {
    bool up;
    bool down;
  } internal;
};

#define INPUT_SIGNAL_PASS(BINDING) (struct InputSignal) { .type = InputSignal_Pass, .bindings[0] = BINDING }
#define INPUT_SIGNAL_UP(BINDING) (struct InputSignal) { .type = InputSignal_Up, .bindings[0] = BINDING }
#define INPUT_SIGNAL_DOWN(BINDING) (struct InputSignal) { .type = InputSignal_Down, .bindings[0] = BINDING }
#define INPUT_SIGNAL_DIRECTION(BINDING_X, BINDING_Y) (struct InputSignal) { .type = InputSignal_Direction, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y }
#define INPUT_SIGNAL_POINT(BINDING_X, BINDING_Y) (struct InputSignal) { .type = InputSignal_Point, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y }
#define INPUT_SIGNAL_VIEWPORT_POINT(BINDING_X, BINDING_Y, CAMERA) (struct InputSignal) { .type = InputSignal_ViewportPoint, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y, .click_camera = CAMERA }

void Engine_set_player_input_backend(uint32_t player_index, const struct InputBackendPair * pairs, uint32_t pair_count);

uint32_t Engine_add_input_frontend(uint32_t player_index, struct InputSignal * signals, uint32_t signal_count);
void Engine_remove_input_frontend(uint32_t player_index, uint32_t index);

#endif