// ::Platform::Linux::Globals::Start::

static pPlatformWindow linux_window = {
	.w = 1920,
	.h = 1080,
};

b32 global_quit = false;

// ::Platform::Linux::Globals::End::

// ::Platform::Linux::Print::Functions::Start::

i32 pWriteStdOut(rawptr buf, i32 len)
{
	return (i32)write(STDOUT, buf, len);
}

i32 pWriteStdErr(rawptr buf, i32 len)
{
	return (i32)write(STDERR, buf, len);
}

// ::Platform::Linux::Window::Functions::Start::

void pWindowEventsGet(pGameInput *inputs, u32 *i_count)
{
	pWindowEventHandle(inputs, i_count, false);
}

b32 pWindowEventWaitFor(pGameInput *input)
{
	u32 i_count;
	pWindowEventHandle(input, &i_count, true);
	return i_count > 0;
}

void pWindowEventHandle(pGameInput *inputs, u32 *i_count, b32 wait_for_event)
{
	b32 has_max_inputs = false;
	*i_count = 0;

	do 
	{
		xcb_generic_event_t *e;
		if (wait_for_event)
			e = xcb_wait_for_event(linux_window.connection);
		else 
			e = xcb_poll_for_event(linux_window.connection);
		
		// XCB_UNMAP_NOTIFY
		if (e != NULL)
		{
			switch (e->response_type & ~0x80)
			{
				case XCB_CLIENT_MESSAGE:
					{
						xcb_client_message_event_t *msg = (xcb_client_message_event_t *)e;
						if (msg->window != linux_window.window)
							break;

						if (msg->data.data32[0] == linux_window.close_event)
							global_quit = true;
					} break;
				case XCB_CONFIGURE_NOTIFY:
					{
						xcb_configure_notify_event_t *configure_event = (xcb_configure_notify_event_t *)e;

						if (linux_window.w != configure_event->width || linux_window.h != configure_event->height)
						{
							linux_window.w = configure_event->width;
							linux_window.h = configure_event->height;
						}
					} break;
				case XCB_KEY_RELEASE:
				case XCB_KEY_PRESS:
					{
						xcb_key_press_event_t *keyboard_e = (xcb_key_press_event_t *)e;
						b8 pressed = e->response_type == XCB_KEY_PRESS;
						xcb_keycode_t code = keyboard_e->detail;
						KeySym keysym = XkbKeycodeToKeysym(linux_window.display, (KeyCode)code, 0, 0);
						pKeyboardInput input = pInputEventConvert(keysym);
						if (input != KB_NONE)
						{
							inputs[*i_count].kb_code = input;
							inputs[*i_count].pressed = pressed;
							inputs[*i_count].type = GI_KEYBOARD;
							*i_count += 1;

							if (*i_count == 10)
								has_max_inputs = true;
						}

					} break;
				case XCB_BUTTON_PRESS: 
				case XCB_BUTTON_RELEASE:
					{
						xcb_button_press_event_t *mouse_ev = (xcb_button_press_event_t *)e;
						b8 pressed = e->response_type == XCB_BUTTON_PRESS;
						pMouseInput input = M_NONE;
						
						if (mouse_ev->detail == XCB_BUTTON_INDEX_1)
							input = M_LEFT_CLICK;
						else if (mouse_ev->detail == XCB_BUTTON_INDEX_2)
							input = M_MIDDLE_CLICK;
						else if (mouse_ev->detail == XCB_BUTTON_INDEX_3)
							input = M_RIGHT_CLICK;

						if (input != M_NONE)
						{
							inputs[*i_count].m_code = input;
							inputs[*i_count].pressed = pressed;
							inputs[*i_count].type = GI_MOUSE;
							*i_count += 1;

							if (*i_count == 10)
								has_max_inputs = true;
						}
					} break;
				case XCB_MOTION_NOTIFY: 
					{
						xcb_motion_notify_event_t *move_ev = (xcb_motion_notify_event_t *)e;
						
						if (move_ev->event_x > 0 || move_ev->event_y > 0)
						{
							inputs[*i_count].motion_ev.x = move_ev->event_x;
							inputs[*i_count].motion_ev.y = move_ev->event_y;
							inputs[*i_count].type = GI_MOTION;

							*i_count += 1;
							if (*i_count == 10)
								has_max_inputs = true;
						}
					}
				default:
					break;
			}

			free(e);
		}
		else
		{
			break;
		}
	} while(!wait_for_event && !has_max_inputs);
}

pKeyboardInput pInputEventConvert(u32 x_key)
{
	switch (x_key)
	{
		case XK_BackSpace: 		return KB_BACKSPACE;
		case XK_Return: 			return KB_ENTER;
		case XK_Tab: 					return KB_TAB;
		case XK_Pause:				return KB_PAUSE;
		case XK_Caps_Lock: 		return KB_CAPS_LOCK;
		case XK_Escape:				return KB_ESC;
		case XK_space: 				return KB_SPACE;
		case XK_Prior:				return KB_PAGE_UP;
		case XK_Next:					return KB_PAGE_DOWN;
		case XK_End:					return KB_END;
		case XK_Home:					return KB_HOME;
		case XK_Left:					return KB_LEFT;
		case XK_Up:						return KB_UP;
		case XK_Right:				return KB_RIGHT;
		case XK_Down:					return KB_DOWN;
		case XK_Print:				return KB_PRINT_SCREEN;
		case XK_Insert:				return KB_INSERT;
		case XK_Delete: 			return KB_DELETE;
		case XK_Meta_L:
		case XK_Super_L:			return KB_LEFT_SUPER;
		case XK_Meta_R:
		case XK_Super_R: 			return KB_RIGHT_SUPER;
		case XK_KP_0:					return KB_NUM_0;
		case XK_KP_1: 				return KB_NUM_1;
		case XK_KP_2: 				return KB_NUM_2;
		case XK_KP_3: 				return KB_NUM_3;
		case XK_KP_4: 				return KB_NUM_4;
		case XK_KP_5: 				return KB_NUM_5;
		case XK_KP_6: 				return KB_NUM_6;
		case XK_KP_7: 				return KB_NUM_7;
		case XK_KP_8: 				return KB_NUM_8;
		case XK_KP_9: 				return KB_NUM_9;
		case XK_multiply:			return KB_NUM_STAR;
		case XK_KP_Subtract:	return KB_NUM_MINUS;
		case XK_KP_Decimal:		return KB_NUM_PERIOD;
		case XK_KP_Divide:		return KB_NUM_SLASH;
		case XK_KP_Add:				return KB_NUM_PLUS;
		case XK_F1:						return KB_F1;
		case XK_F2:						return KB_F2;
		case XK_F3:						return KB_F3;
		case XK_F4:						return KB_F4;
		case XK_F5:						return KB_F5;
		case XK_F6:						return KB_F6;
		case XK_F7:						return KB_F7;
		case XK_F8:						return KB_F8;
		case XK_F9:						return KB_F9;
		case XK_F10:					return KB_F10;
		case XK_F11:					return KB_F11;
		case XK_F12:					return KB_F12;
		case XK_Num_Lock:			return KB_NUM_LOCK;
		case XK_Scroll_Lock:	return KB_SCROLL_LOCK;
		case XK_Shift_L:			return KB_LEFT_SHIFT;
		case XK_Shift_R: 			return KB_RIGHT_SHIFT;
		case XK_Control_L:		return KB_LEFT_CTRL;
		case XK_Control_R:		return KB_RIGHT_CTRL;
		case XK_Alt_L: 				return KB_LEFT_ALT;
		case XK_Alt_R:				return KB_RIGHT_ALT;
		case XK_semicolon:		return KB_SEMICOLON;
		case XK_bracketleft:	return KB_LEFT_BRACE;
		case XK_bracketright:	return KB_RIGHT_BRACE;
		case XK_plus:					return KB_PLUS;
		case XK_comma: 				return KB_COMMA;
		case XK_minus:				return KB_MINUS;
		case XK_backslash:		return KB_BACK_SLASH;
		case XK_slash:				return KB_FORWARD_SLASH;
		case XK_grave: 				return KB_TILDE;
		case XK_0:						return KB_0;
		case XK_1:						return KB_1;
		case XK_2:						return KB_2;
		case XK_3:						return KB_3;
		case XK_4:						return KB_4;
		case XK_5:						return KB_5;
		case XK_6:						return KB_6;
		case XK_7:						return KB_7;
		case XK_8:						return KB_8;
		case XK_9:						return KB_9;
		case XK_a:
		case XK_A:						return KB_A;
		case XK_b:
		case XK_B:						return KB_B;
		case XK_c:
		case XK_C:						return KB_C;
		case XK_d:
		case XK_D:						return KB_D;
		case XK_e:
		case XK_E:						return KB_E;
		case XK_f:
		case XK_F:						return KB_F;
		case XK_g:
		case XK_G:						return KB_G;
		case XK_h:
		case XK_H:						return KB_H;
		case XK_i:
		case XK_I:						return KB_I;
		case XK_j:
		case XK_J:						return KB_J;
		case XK_k:
		case XK_K:						return KB_K;
		case XK_l:
		case XK_L:						return KB_L;
		case XK_m:
		case XK_M:						return KB_M;
		case XK_n:
		case XK_N:						return KB_N;
		case XK_o:
		case XK_O:						return KB_O;
		case XK_p:
		case XK_P:						return KB_P;
		case XK_q:
		case XK_Q:						return KB_Q;
		case XK_r:
		case XK_R:						return KB_R;
		case XK_s:
		case XK_S:						return KB_S;
		case XK_t:
		case XK_T:						return KB_T;
		case XK_u:
		case XK_U:						return KB_U;
		case XK_v:
		case XK_V:						return KB_V;
		case XK_w:
		case XK_W:						return KB_W;
		case XK_x:
		case XK_X:						return KB_X;
		case XK_y:
		case XK_Y:						return KB_Y;
		case XK_z:
		case XK_Z:						return KB_Z;
		default: 							return KB_NONE;
	}
}

// ::Platform::Linux::Window::Functions::Start::



// ::Platform::Linux::Utils::Functions::Start::

b32 pSyscallErrCheck(void *ptr) 
{
	return (isize)ptr == SYS_ERR ? true : false;
}

// ::Platform::Linux::Utils::Functions::End::



// ::Platform::Linux::Async::Start::

static pThread pThreadInit(rawptr proc, rawptr param)
{
	pThread thread = {0};
	Assert(pthread_mutex_init(&thread.mut, NULL) == 0, "pthread_mutex_init failure");
	Assert(pthread_cond_init(&thread.cond, NULL) == 0, "pthread_cond_init failure");
	Assert(pthread_create(&thread.handle, NULL, proc, param) == 0, "pthread_create failure");

	return thread;
}

static void pThreadSuspend(pThread *thread)
{
	pthread_mutex_lock(&thread->mut);
	pthread_cond_wait(&thread->cond, &thread->mut);
	pthread_mutex_unlock(&thread->mut);
}

static void pThreadWake(pThread *thread)
{
	pthread_cond_signal(&thread->cond);
}

static void pThreadKill()
{
	pthread_exit(NULL);
}

// ::Platform::Linux::Async::Start::



// ::Platform::Linux::Includes::CFile::

#include "platform_linux_public.c"


