#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>

struct app {
	Display *dis;
	int32_t screen;
	Window root_win;
	GC gc;
	struct {
		Window it;
		struct {
			uint32_t w;
			uint32_t h;
		} size;
		struct {
			int32_t x;
			int32_t y;
		} pos;
	} win;
	struct {
		uint64_t black;
		uint64_t white;
	} pixel;
};

enum app_err_code {
	APP_ERR_OK = 0,
	APP_ERR_FAILED_OPEN_DISPLAY
};

struct app_err {
	enum app_err_code code;
	const char *name;
};

struct app app_init(uint32_t width, uint32_t height, struct app_err *err);
void app_close(struct app app);

void _app_err_set(struct app_err *err, enum app_err_code code);

int
main(int argc, char *argv[])
{
	struct app_err aerr = {0};
	struct app app = {0};

	app = app_init(800, 600, &aerr);
	if ( aerr.code != APP_ERR_OK ) {
		fprintf(stderr, "Failed to init app: `%s`\n", aerr.name);
		exit(EXIT_FAILURE);
	}

	app_close(app);
	printf("Hello World!\n");
	(void) argc; (void) argv;
	return 0;
}

struct app
app_init(uint32_t width, uint32_t height, struct app_err *err)
{
	struct app empty_app = {0};
	struct app app = {0};

	app.win.size.w = width;
	app.win.size.h = height;

	app.dis = XOpenDisplay(NULL);
	if ( app.dis == NULL ) {
		_app_err_set(err, APP_ERR_FAILED_OPEN_DISPLAY);
		return empty_app;
	}
	app.screen = XDefaultScreen(app.dis);

	app.root_win = XDefaultRootWindow(app.dis);

	app.pixel.black = XBlackPixel(app.dis, app.screen);
	app.pixel.white = XWhitePixel(app.dis, app.screen);

	app.win.it = XCreateSimpleWindow(app.dis, app.root_win,
					app.win.pos.x, app.win.pos.y,
					app.win.size.w, app.win.size.h,
					5, app.pixel.black, app.pixel.white);

	XSetStandardProperties(app.dis, app.win.it, "Window", "Minimized",
				None, NULL, 0, NULL);

	XSelectInput(app.dis, app.win.it, ExposureMask | ButtonPressMask | KeyPressMask);

	app.gc = XCreateGC(app.dis, app.win.it, 0, 0);

	XSetBackground(app.dis, app.gc, app.pixel.white);
	XSetForeground(app.dis, app.gc, app.pixel.black);

	XClearWindow(app.dis, app.win.it);
	XMapRaised(app.dis, app.win.it);

	return app;
}

void
app_close(struct app app)
{
	XFreeGC(app.dis, app.gc);
	XDestroyWindow(app.dis, app.win.it);
	XCloseDisplay(app.dis);
}

void 
_app_err_set(struct app_err *err, enum app_err_code code)
{
	if ( err == NULL ) {
		return ;
	}

	err->code = code;
	switch ( code ) {
#define _APP_ERR_SET_CASE(code) case code: err->name = #code; break

	_APP_ERR_SET_CASE(APP_ERR_OK);
	_APP_ERR_SET_CASE(APP_ERR_FAILED_OPEN_DISPLAY);

#undef _APP_ERR_SET_CASE
	}
}
