#pragma once

// ::Platform::Types::Header::

typedef struct pLibrary pLibrary;
typedef struct pFunction pFunction;

// ::Platform::Declarations::Header::

typedef struct pGameInput pGameInput;

typedef enum KeyboardInput_e
{
	KB_NONE, 
	KB_A, KB_B, KB_C, KB_D, KB_E, KB_F, KB_G, KB_H, KB_I, KB_J, KB_K, KB_L, KB_M, 
	KB_N, KB_O, KB_P, KB_Q, KB_R, KB_S, KB_T, KB_U, KB_V, KB_W, KB_X, KB_Y, KB_Z, 
	KB_0, KB_1, KB_2, KB_3, KB_4, KB_5, KB_6, KB_7, KB_8, KB_9,
	KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9,
	KB_NUM_LOCK, KB_NUM_SLASH, KB_NUM_STAR, KB_NUM_MINUS, KB_NUM_PLUS, KB_NUM_ENTER, KB_NUM_PERIOD,
	KB_INSERT, KB_DELETE, KB_HOME, KB_END, KB_PAGE_UP, KB_PAGE_DOWN, 
	KB_PRINT_SCREEN, KB_SCROLL_LOCK, KB_PAUSE,
	KB_COMMA, KB_PERIOD, KB_BACK_SLASH, KB_BACKSPACE, KB_FORWARD_SLASH, KB_MINUS, KB_PLUS,
	KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12, 
	KB_UP, KB_DOWN, KB_LEFT, KB_RIGHT,
	KB_LEFT_CTRL, KB_LEFT_ALT, KB_LEFT_SHIFT, KB_LEFT_SUPER,
	KB_TAB, KB_CAPS_LOCK,
	KB_RIGHT_CTRL, KB_RIGHT_ALT, KB_RIGHT_SUPER, KB_RIGHT_SHIFT,
	KB_ENTER, KB_SPACE,
	KB_TILDE, KB_ESC,
	KB_SEMICOLON, KB_QUOTE, KB_LEFT_BRACE, KB_RIGHT_BRACE, KB_BACK_SPACE,

	KB_MAX
} pKeyboardInput;

// ::Platform::Includes::Header::

#if __linux__
#include "platform/platform_linux.h"
#endif

#if _WIN32
#include "platform/platform_windows.h"
#endif

#if __APPLE__ || __MACH__
#error Not yet implemented
#endif

#if __unix__ && !__linux__
#error Not yet implemented
#endif

// ::Platform::Enum::Header::

typedef enum MouseInput_e
{
	M_NONE,
	M_LEFT_CLICK, 
	M_MIDDLE_CLICK, 
	M_RIGHT_CLICK,
} pMouseInput;

typedef enum GameInputType_e
{
	GI_NONE,
	GI_KEYBOARD, 
	GI_MOUSE, 
	GI_MOTION, 
	GI_GAMEPAD,
} pGameInputType;

// ::Platform::Types::Header::


typedef u16Vec2 pWindowSize;

typedef i16Vec2 pMouseMotion;

typedef struct pGameInput
{
	union 
	{
		pKeyboardInput kb_code;
		pMouseInput m_code;
		pMouseMotion motion_ev;
	};
	b8 pressed;
	pGameInputType type;
}	pGameInput; // TODO: add gamepad input

typedef struct pThread pThread;

// ::Platform::ConsoleOut::Functions::Header::

i32 pWriteStdOut(void *buf, i32 len);
i32 pWriteStdErr(void *buf, i32 len);

// ::Platform::pLibrary::Functions::Header::

b32 pLibraryLoad(const char *name, pLibrary *out_lib);
b32 pFunctionLoad(const char *name, pLibrary *lib, pFunction *out_fn);

// ::Platform::Memory::Functions::Header::

rawptr pMemAlloc(usize size);
rawptr pMemAllocZeroed(usize size);
rawptr pMemRealloc(rawptr ptr, usize old_size, usize new_size);
void pMemFree(rawptr ptr, usize size);
usize pPageSize();

// ::Platform::Window::Functions::Header::

b32 pWindowInit(const char *window_name);
pWindowSize pWindowGetSize();
b32 pWindowShouldQuit();
pPlatformWindow *pWindowGet();

// ::Platform::FileSystem::Functions::Header::

b32 pDirNavigate(c8 *dir);
c8 **pDirGetFileNames(Arena *arena, u32 *count);
static b8 pDirIsVisible(c8 *dir_name);

// ::Platform::Profiling::Functions::Header::

static u64 pOSTimerFreq();
static u64 pOSTimerRead();
static inline u64 pCPUTimerRead();

// ::Platform::Async::Functions::Header::

static pThread pThreadInit(rawptr proc, rawptr param);
static void pThreadSuspend(pThread *thread);
static void pThreadWake(pThread *thread);
static void pThreadKill();

// ::Platform::Atomics::Header::

static inline void pAtomicSignalFenceSeqCst();
static inline u32 pAtomicFetchSubU32(u32 volatile *ptr, u32 count);
static inline u32 pAtomicFetchIncrU32(u32 volatile *ptr);
static inline void pAtomicIncrU8(u8 volatile *ptr);
static inline void pAtomicIncrU32(u32 volatile *ptr);
static inline u32 pAtomicLoadU32(u32 volatile *ptr);
static inline void pAtomicStoreB32(b32 volatile *ptr, b32 value);
static inline b32 pAtomicCompareExchangeB32(b32 volatile *ptr, b32 *expect, b32 desired);

