set -eu
camellia_path_check

fetch() {
	git clone https://github.com/ozkl/doomgeneric
	cd doomgeneric
	# TODO use a newer commit
	git -c advice.detachedHead=false checkout ee3ee581912dacd38af06e81da2374c1267453e8

	cd doomgeneric
	rm doomgeneric # a leftover OS X binary
	sed s/xlib/camellia/ -i Makefile
	sed s/-lX11// -i Makefile
	echo note: you need to supply your own WADs

	cat <<\EOF > doomgeneric_camellia.c
#include <camellia/syscalls.h>
#include <shared/ring.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <draw.h>
#include <thread.h>

#include "doomgeneric.h"
#include "doomkeys.h"

static struct framebuf fb;
static hid_t kb;

static const char keymap_lower[] = {
	'\0', '\0', '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
	'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  'o',  'p',  '[',  ']',  '\r', '\0', 'a',  's',
	'd',  'f',  'g',  'h',  'j',  'k',  'l',  '\0', '\'', '`',  '\0', '\\', 'z',  'x',  'c',  'v',
	'b',  'n',  'm',  ',',  '.',  '/',  '\0', '*',  '\0', ' ',  '\0', '\0', '\0', '\0', '\0', '\0',
	'\0', '\0', '\0', '\0', '\0', '\0', '\0', '7',  '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
	'2',  '3',  '0',  '.',  '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
	'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
	'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
};

static char kb_backbuf[256];
static ring_t kb_backlog = {kb_backbuf, sizeof kb_backbuf, 0, 0};

static void kb_thread(void *arg) {
	(void)arg;
	char buf[sizeof kb_backbuf];
	for (;;) {
		int ret = _sys_read(kb, buf, sizeof buf, 0);
		if (ret <= 0) break;
		ring_put(&kb_backlog, buf, ret);
	}
}

void DG_Init(void) {
	if (fb_setup(&fb, "/kdev/video/") < 0) {
		puts("DG_Init: fb_setup error");
		abort();
	}
	kb = _sys_open("/kdev/ps2/kb", 12, 0);
	if (kb < 0) {
		puts("DG_Init: can't open keyboard");
		abort();
	}
	thread_create(0, kb_thread, NULL);
}

void DG_DrawFrame(void) {
	size_t doomw = DOOMGENERIC_RESX;
	size_t doomh = DOOMGENERIC_RESY;
	size_t doompitch = doomw * 4;
	for (int y = 0; y < DOOMGENERIC_RESY; y++) {
		memcpy(fb.b + fb.pitch * y, ((char*)DG_ScreenBuffer) + doompitch * y, doompitch);
	}

	struct rect d;
	dirty_mark(&d, 0, 0);
	dirty_mark(&d, doomw, doomh);
	dirty_flush(&d, &fb);
}

static uint32_t timer = 0;
void DG_SleepMs(uint32_t ms) {
	timer += ms;
	_sys_sleep(ms);
}

uint32_t DG_GetTicksMs(void) {
	/* if it's stupid and it works... */
	return timer;
}

int DG_GetKey(int *pressed, unsigned char *key) {
	uint8_t c;
	int read = ring_get(&kb_backlog, (char*)&c, 1);
	bool extended = c == 0xe0;;
	if (read == 0) return 0;
	if (extended) ring_get(&kb_backlog, (char*)&c, 1);
	bool down = !(c & 0x80);

	c &= 0x7f;
	if (!extended) {
		switch (c) {
			case 0x01:
				c = KEY_ESCAPE;
				break;
			case 0x2a: case 0x36:
				c = KEY_RSHIFT;
				break;
			case 0x1d:
				c = KEY_FIRE;
				break;
			default:
				c = keymap_lower[c & 0x7f];
				switch (c) {
					case ' ': c = KEY_USE; break;
				}
		}
	} else {
		switch (c) {
			case 0x48: c = KEY_UPARROW; break;
			case 0x50: c = KEY_DOWNARROW; break;
			case 0x4B: c = KEY_LEFTARROW; break;
			case 0x4D: c = KEY_RIGHTARROW; break;
			default:   c = '\0'; break;
		}
	}
	*pressed = down;
	*key = c;
	return 1;
}

void DG_SetWindowTitle(const char *title) {
	(void)title;
}
EOF
}

prep() {
	[ -d doomgeneric ] || (fetch)
	cd doomgeneric/doomgeneric
}

case $1 in
	install) (prep; make "CC=x86_64-camellia-gcc" && cp doomgeneric $PREFIX/bin/doom && cp *.WAD $PREFIX/) ;;
	clean)   (prep; make clean) ;;
	deepclean) (rm -rf doomgeneric) ;;
	*)       echo "usage: $0 install|clean"; false ;;
esac