import aliases;
import includes;
import std.stdio;
import core.memory;

version(linux)
{
import core.sys.posix.dlfcn;

struct Window
{
	Display 				 *display;
	xcb_connection_t *conn;
	xcb_window_t 			window;
	xcb_atom_t				close_event;
	xcb_atom_t				minimize_event;
	u16								w;
	u16								h;
	bool							close;
};

struct Library
{
	void* ptr;
};

struct Function
{
	void* ptr;
};

void CheckErr(Window *window, xcb_void_cookie_t *cookie, xcb_generic_error_t *err, string msg)
{
	assert(err == null, msg);
	pureFree(err);
};

Window CreateWindow(string name, u16 width, u16 height)
{
	Window window = {
		w: width,
		h: height,
	};

	assert(width > 0 && height > 0, "CreateWindow error: width and height must be above 0");
	
	window.display = XOpenDisplay(null);
	assert(window.display != null, "XOpenDisplay failure");

	window.conn = XGetXCBConnection(window.display);
	assert(window.conn != null, "XGetXCBConnection failure");

	xcb_void_cookie_t cookie;
	xcb_generic_error_t *error;

	xcb_setup_t *setup = xcb_get_setup(window.conn);
	xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
	xcb_screen_t *screen = iter.data;

	i32 event_mask = XCB_EVENT_MASK_EXPOSURE				|
									 XCB_EVENT_MASK_KEY_PRESS				|
									 XCB_EVENT_MASK_KEY_RELEASE			|
									 XCB_EVENT_MASK_BUTTON_PRESS		|
									 XCB_EVENT_MASK_BUTTON_RELEASE	|
									 XCB_EVENT_MASK_POINTER_MOTION	|
									 XCB_EVENT_MASK_STRUCTURE_NOTIFY;

	i32[] val_win = [screen.black_pixel, event_mask];
	i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;

	window.window = xcb_generate_id(window.conn);

	cookie = xcb_create_window_checked(
		window.conn,
		XCB_COPY_FROM_PARENT,
		window.window,
		screen.root,
		0, // x pos
		0, // y pos
		window.w, // width
		window.h, // height
		0,
		XCB_WINDOW_CLASS_INPUT_OUTPUT,
		screen.root_visual,
		val_mask,
		val_win.ptr
	);
	error = xcb_request_check(window.conn, cookie);
	CheckErr(&window, &cookie, error, "xcb_create_window failure");

	cookie = xcb_map_window_checked(window.conn, window.window);
	error = xcb_request_check(window.conn, cookie);
	CheckErr(&window, &cookie, error, "xcb_map_window_checked failure");

	cookie = xcb_change_property_checked(
		window.conn,
		XCB_PROP_MODE_REPLACE,
		window.window,
		XCB_ATOM_WM_NAME,
		XCB_ATOM_STRING,
		8,
		cast(u32)name.length,
		name.ptr
	);
	error = xcb_request_check(window.conn, cookie);
	CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");

	xcb_intern_atom_cookie_t c_proto = xcb_intern_atom(window.conn, 1, 12, "WM_PROTOCOLS");
	xcb_intern_atom_reply_t *r_proto = xcb_intern_atom_reply(window.conn, c_proto, &error);
	CheckErr(&window, &cookie, error, "xcb_intern_atom WM_PROTOCOLS failure");

	xcb_intern_atom_cookie_t c_close = xcb_intern_atom(window.conn, 0, 16, "WM_DELETE_WINDOW");
	xcb_intern_atom_reply_t *r_close = xcb_intern_atom_reply(window.conn, c_close, &error);
	CheckErr(&window, &cookie, error, "xcb_intern_atom WM_DELETE_WINDOW failure");

	xcb_intern_atom_cookie_t c_minimize = xcb_intern_atom(window.conn, 0, 20, "_NET_WM_STATE_HIDDEN");
	xcb_intern_atom_reply_t *r_minimize = xcb_intern_atom_reply(window.conn, c_minimize, &error);
	CheckErr(&window, &cookie, error, "xcb_intern_atom _NET_WM_STATE_HIDDEN failure");


	cookie = xcb_change_property_checked(
		window.conn,
		XCB_PROP_MODE_REPLACE,
		window.window,
		r_proto.atom,
		XCB_ATOM_ATOM,
		32,
		1,
		&r_close.atom
	);
	error = xcb_request_check(window.conn, cookie);
	CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");

	window.close_event = r_close.atom;
	window.minimize_event = r_minimize.atom;

	pureFree(r_proto);
	pureFree(r_close);
	pureFree(r_minimize);

	xcb_map_window(window.conn, window.window);

	i32 stream_result = xcb_flush(window.conn);
	assert(stream_result > 0, "xcb_flush failure");

	return window;
};

void
HandleEvents(Window* window)
{
	xcb_generic_event_t* e;
	
	do
	{
		e = xcb_poll_for_event(window.conn);

		if (e)
		{
			switch (e.response_type & ~0x80)
			{
			case XCB_CLIENT_MESSAGE:
				{
					xcb_client_message_event_t* msg = cast(xcb_client_message_event_t*)e;
					if (msg.window != window.window)
					{
						break;
					}

					if (msg.data.data32[0] == window.close_event)
					{
						window.close = true;
					}
				} break;
			default:
				break;
			}
		}
	} 
	while (e);
}

Library LoadLibrary(string name)
{
	Library lib = {
		ptr: null,
	};

	lib.ptr = dlopen(name.ptr, RTLD_NOW);
	
	return lib;
};

Function LoadFunction(Library lib, string name)
{
	Function fn = {
		ptr: null,
	};

	fn.ptr = dlsym(lib.ptr, name.ptr);

	return fn;
};

}

version(Windows)
{

}
