#ifndef MR4TH_OSW_H
#define MR4TH_OSW_H

////////////////////////////////
// Macros: OSW Configuration

#if !defined(MR4TH_OSW_MAX_WINDOW_COUNT)
# define MR4TH_OSW_MAX_WINDOW_COUNT 64
#endif
#if !defined(MR4TH_OSW_MAX_WINDOW_TITLE_SIZE)
# define MR4TH_OSW_MAX_WINDOW_TITLE_SIZE 1023
#endif

// default osw-impl from os
#if OS_WINDOWS
# define OSW_IMPL_WIN32 1
#elif OS_LINUX
# if !defined(OSW_IMPL_X11) || !OSW_IMPL_X11
#  define OSW_IMPL_WAYLAND 1
# endif
#endif

// auto-zero osw-impls
#if !defined(OSW_IMPL_WIN32)
# define OSW_IMPL_WIN32 0
#endif
#if !defined(OSW_IMPL_X11)
# define OSW_IMPL_X11 0
#endif
#if !defined(OSW_IMPL_WAYLAND)
# define OSW_IMPL_WAYLAND 0
#endif

#if (OSW_IMPL_WIN32 + OSW_IMPL_X11 + OSW_IMPL_WAYLAND) == 0
# error no osw implementation selected
#endif
#if (OSW_IMPL_WIN32 + OSW_IMPL_X11 + OSW_IMPL_WAYLAND) > 1
# error multiple osw implementations selected
#endif

////////////////////////////////
// Types: OSW Implementable

typedef struct OSW_Window OSW_Window;

////////////////////////////////
// Types: OSW

typedef U32 OSW_Cap;
enum{
  OSW_Cap_AbsoluteCoordinates = 1,
  OSW_Cap_CustomBorders = 2,
};

typedef struct OSW_WindowBase{
  B32 allocated;
  OSW_Window *next;
  OSW_Window *prev;
} OSW_WindowBase;

typedef struct OSW_Backend{
  B32 initialized;
  void (*close)(void);
  void (*begin)(OSW_Window *window);
  void (*end)(OSW_Window *window);
} OSW_Backend;

// cursor shape

typedef U32 OSW_CursorShape;
enum{
  OSW_CursorShape_Hidden,
  OSW_CursorShape_Default,
  OSW_CursorShape_Pointer,
  OSW_CursorShape_TextInsert,
  OSW_CursorShape_Loading,
  OSW_CursorShape_CrossHair,
  OSW_CursorShape_Move,
  OSW_CursorShape_Resize_T,
  OSW_CursorShape_Resize_B,
  OSW_CursorShape_Resize_L,
  OSW_CursorShape_Resize_R,
  OSW_CursorShape_Resize_TL,
  OSW_CursorShape_Resize_TR,
  OSW_CursorShape_Resize_BL,
  OSW_CursorShape_Resize_BR,
  OSW_CursorShape_COUNT
};

// event kind

#define OSW_EventKindXList(X)\
X(Null)\
X(WindowClose)\
X(WindowLoseFocus)\
X(Key)\
X(TextInsert)\
X(MouseButton)\
X(MouseWheel)\
X(FileDrop)

typedef enum OSW_EventKind{
#define X(N) OSW_EventKind_##N,
  OSW_EventKindXList(X)
#undef X
  OSW_EventKind_COUNT
} OSW_EventKind;

// event flags

typedef U32 OSW_EventFlags;
enum{
  OSW_EventFlag_Press   = (1 << 0),
  OSW_EventFlag_Release = (1 << 1),
  OSW_EventFlag_Repeat  = (1 << 2),
#define OSW_EventFlag_TransitionShift 0
#define OSW_EventFlag_TransitionMask  0x7
  
  OSW_EventFlag_Shift   = (1 << 3),
  OSW_EventFlag_Control = (1 << 4),
  OSW_EventFlag_Alt     = (1 << 5),
  OSW_EventFlag_Command = (1 << 6),
#define OSW_EventFlag_ModifierShift 3
#define OSW_EventFlag_ModifierMask  0xF
};

// key kinds

#define OSW_KeyXList(XX)\
XX(Null)\
XX(Escape)\
XX(F1)   XX(F2)  XX(F3)  XX(F4)\
XX(F5)   XX(F6)  XX(F7)  XX(F8)\
XX(F9)  XX(F10) XX(F11) XX(F12)\
XX(F13) XX(F14) XX(F15) XX(F16)\
XX(F17) XX(F18) XX(F19) XX(F20)\
XX(F21) XX(F22) XX(F23) XX(F24)\
XX(F25) XX(F26) XX(F27) XX(F28)\
XX(F29) XX(F30) XX(F31) XX(F32)\
XX(Backspace)\
XX(Tab)\
XX(CapsLock)\
XX(Return)\
XX(Shift)\
XX(Control)\
XX(Alt)\
XX(Command)\
XX(Context)\
XX(Space)\
XX(PrintScreen)\
XX(ScrollLock)\
XX(PauseBreak)\
XX(Insert)\
XX(Delete)\
XX(Home)\
XX(End)\
XX(PageUp)\
XX(PageDown)\
XX(Up) XX(Left) XX(Down) XX(Right)\
XX(NumLock)\
XX(A) XX(B) XX(C) XX(D) XX(E) XX(F) XX(G) XX(H) XX(I) XX(J) XX(K) XX(L) XX(M)\
XX(N) XX(O) XX(P) XX(Q) XX(R) XX(S) XX(T) XX(U) XX(V) XX(W) XX(X) XX(Y) XX(Z)\
XX(0) XX(1) XX(2) XX(3) XX(4) XX(5) XX(6) XX(7) XX(8) XX(9)\
XX(Tick) XX(Minus) XX(Equals) XX(LeftBracket) XX(RightBracket) XX(BackSlash)\
XX(Semicolon) XX(Quote) XX(Comma) XX(Period) XX(Slash)\
XX(Num0) XX(Num1) XX(Num2) XX(Num3) XX(Num4)\
XX(Num5) XX(Num6) XX(Num7) XX(Num8) XX(Num9)\
XX(NumMultiply)\
XX(NumAdd)\
XX(NumSubtract)\
XX(NumDecimal)\
XX(NumDivide)

typedef enum OSW_Key{
#define X(N) OSW_Key_##N,
  OSW_KeyXList(X)
#undef X
  OSW_Key_COUNT
} OSW_Key;

// mouse

#define OSW_MouseButtonXList(X)\
X(Null)\
X(Left)\
X(Right)\
X(Middle)

typedef enum OSW_MouseButton{
#define X(N) OSW_MouseButton_##N,
  OSW_MouseButtonXList(X)
#undef X
  OSW_MouseButton_COUNT
} OSW_MouseButton;

// events

typedef struct OSW_FileDropData{
  String8List files;
  S32 point[2];
} OSW_FileDropData;

typedef struct OSW_Event{
  struct OSW_Event *next;
  struct OSW_Event *prev;
  OSW_EventKind kind;
  OSW_EventFlags flags;
  OSW_Window *window;
  U32 code;
  F32 delta;
  union{
    OSW_FileDropData *file_drop_data;
  };
} OSW_Event;

// input

typedef struct OSW_Input{
  OSW_Event *first_event;
  OSW_Event *last_event;
  U32 event_count;
} OSW_Input;



////////////////////////////////
// Macros: OSW Window Check

// TODO(allen): I want a full handle validator since I'm reading user data's
// off of retained objects.
#define osw_window_is_valid(w) ((w) != 0 && (w)->base.allocated)

////////////////////////////////
// Functions: OSW Implementable

// global

MR4TH_SYMBOL void osw_init(void);
MR4TH_SYMBOL B32  osw_has_capability(OSW_Cap cap);

MR4TH_SYMBOL void osw_tick(Arena *arena, B32 blocking);
MR4TH_SYMBOL void osw_set_cursor_shape(OSW_CursorShape shape);

// windows

MR4TH_SYMBOL OSW_Window* osw_window_create(V2S32 size);
MR4TH_SYMBOL void        osw_window_destroy(OSW_Window *window);

MR4TH_SYMBOL void osw_window_set_title(OSW_Window *window, String8 str);
MR4TH_SYMBOL void osw_window_set_min_size(OSW_Window *window, V2S32 size);
MR4TH_SYMBOL void osw_window_set_max_size(OSW_Window *window, V2S32 size);
MR4TH_SYMBOL void osw_window_set_visible(OSW_Window *window, B32 visible);
MR4TH_SYMBOL void osw_window_set_fullscreen(OSW_Window *window, B32 fullscreen);
MR4TH_SYMBOL void osw_window_set_maximize(OSW_Window *window, B32 maximize);
MR4TH_SYMBOL void osw_window_set_minimize(OSW_Window *window);
MR4TH_SYMBOL void osw_window_set_resizable(OSW_Window *window, B32 resizable);

MR4TH_SYMBOL V2S32 osw_window_get_size(OSW_Window *window);
MR4TH_SYMBOL B32   osw_window_is_visible(OSW_Window *window);
MR4TH_SYMBOL B32   osw_window_is_fullscreen(OSW_Window *window);
MR4TH_SYMBOL B32   osw_window_is_maximized(OSW_Window *window);
MR4TH_SYMBOL B32   osw_window_is_minimized(OSW_Window *window);
MR4TH_SYMBOL B32   osw_window_is_resizable(OSW_Window *window);

// CAP: absolute positions

MR4TH_SYMBOL V2S32   osw_get_absolute_cursor_pos(void);

MR4TH_SYMBOL RectS32 osw_window_get_absolute_outer_rect(OSW_Window *window);
MR4TH_SYMBOL RectS32 osw_window_get_absolute_inner_rect(OSW_Window *window);

// CAP: custom borders

MR4TH_SYMBOL void osw_window_set_custom_border(OSW_Window *window, B32 custom_border);
MR4TH_SYMBOL void osw_window_frame_custom_border_size(OSW_Window *window, RectS32 border_size);

////////////////////////////////
// Functions: OSW Backend Functions

MR4TH_SYMBOL void osw_backend_close(OSW_Backend *backend);

MR4TH_SYMBOL void osw_backend_render_begin(OSW_Backend *backend, OSW_Window *window);
MR4TH_SYMBOL void osw_backend_render_end(void);

////////////////////////////////
// Functions: OSW Event Writer Functions

MR4TH_SYMBOL void       osw_event__write_begin(Arena *arena);
MR4TH_SYMBOL OSW_Event* osw_event__write_push(void);

////////////////////////////////
// Functions: OSW Event Accessor Functions

#define OSW_EachEvent(e) \
OSW_Event *e = osw_event__first(), *e##_next = (e != 0)?(e->next):0; \
e != 0; e = e##_next

MR4TH_SYMBOL void       osw_event_erase(OSW_Event *event);

MR4TH_SYMBOL OSW_Event* osw_event__first(void);

////////////////////////////////
// Functions: OSW Window Allocator Functions

MR4TH_SYMBOL OSW_Window* osw_window__alloc(void);
MR4TH_SYMBOL void        osw_window__release(OSW_Window *window);
MR4TH_SYMBOL OSW_Window* osw_window__first(void);
MR4TH_SYMBOL U32         osw_window__idx_from_ptr(OSW_Window *window);

////////////////////////////////
// Functions: OSW Error Functions

MR4TH_SYMBOL void osw__error_unsupported(OSW_Cap cap);


#endif //MR4TH_OSW_H
