#include "quinn.h"
#include "doomgeneric.h"
#include "doomkeys.h"
#include "doom.xpm"
#include "loading.xpm"

#define KEYQUEUE_SIZE 16

static unsigned short s_KeyQueue[KEYQUEUE_SIZE];
static unsigned int s_KeyQueueWriteIndex = 0;
static unsigned int s_KeyQueueReadIndex = 0;

static int window_handle = 0;
static char *surface;

static unsigned int start_ticks;

void exit_callback(int wh) {

}

void do_exit() {
    quinn_exit();
}

static const char keyChars_set1[] = {
	0, 		27, 	'1', 	'2',
	'3', 	'4', 	'5', 	'6',
	'7', 	'8', 	'9', 	'0',
	'-', 	'=', 	'\b', 	'\t',
	'q', 	'w', 	'e', 	'r',
	't', 	'y', 'u', 	'i',
	'o', 'p', 	'[', 	']',
	'\n', 0, 		'a', 	's',
	'd', 'f',	'g',	'h',
	'j', 'k', 	'l', 	';',
	'\'', '`', 	0, 		'\\',
	'z', 'x', 	'c', 	'v',
	'b', 'n',	'm',	',',
	'.', '/',	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 unsigned char convertToDoomKey(unsigned char scancode)
{
    unsigned char key = 0;

    switch (scancode)
    {
    case 0x1C:
        key = KEY_ENTER;
        break;
    case 0x01:
        key = KEY_ESCAPE;
        break;
    case 0x4B:
        key = KEY_LEFTARROW;
        break;
    case 0x4D:
        key = KEY_RIGHTARROW;
        break;
    case 0x48:
        key = KEY_UPARROW;
        break;
    case 0x50:
        key = KEY_DOWNARROW;
        break;
    case 0x1D:
        key = KEY_FIRE;
        break;
    case 0x39:
        key = KEY_USE;
        break;
    case 0x36:
        key = KEY_RSHIFT;
        break;
    case 0x0E:
        key = KEY_BACKSPACE;
        break;
    default:
        if (scancode > 0 && scancode < 0x57) {
            key = keyChars_set1[scancode];
        }
        break;
    }

    return key;
}

void uninput_cb(char c) {
    unsigned char key = convertToDoomKey((unsigned char)c);

    unsigned short keyData = (0 << 8) | key;

    s_KeyQueue[s_KeyQueueWriteIndex] = keyData;
    s_KeyQueueWriteIndex++;
    s_KeyQueueWriteIndex %= KEYQUEUE_SIZE;
}


void input_cb(char c) {
    unsigned char key = convertToDoomKey((unsigned char)c);

    unsigned short keyData = (1 << 8) | key;

    s_KeyQueue[s_KeyQueueWriteIndex] = keyData;
    s_KeyQueueWriteIndex++;
    s_KeyQueueWriteIndex %= KEYQUEUE_SIZE;
}

void DG_Init() {
    struct window_req_t wr;
    struct widget_t *w;
    char *icon, *loading;
    __asm__ volatile ("int $0x30" : "=a" (start_ticks) : "0" (46));

   // atexit(do_exit);
	convert_xpm(doom_xpm, &icon);
    wr.x = 30;
    wr.y = 30;
    strcpy(wr.name, "DOOM");
    wr.width = DOOMGENERIC_RESX;
    wr.height = DOOMGENERIC_RESY;
    wr.flags = WIN_REQ_FLAG_NO_MOUSE;
    wr.icon = icon;

    window_handle = quinn_req_window(&wr, exit_callback);
    free(icon);

    w = quinn_add_surface(window_handle, 0, 0, DOOMGENERIC_RESX, DOOMGENERIC_RESY, NULL, NULL, input_cb, uninput_cb);
    surface = quinn_surface_get_surface(w);
    convert_xpm(loading_xpm, &loading);
    quinn_fill_rect(window_handle, surface, DOOMGENERIC_RESX, DOOMGENERIC_RESY, 0, 0, DOOMGENERIC_RESX, DOOMGENERIC_RESY, 0xff000000);
    quinn_render(window_handle, surface, loading, 147, 104, 233, 120, DOOMGENERIC_RESX, DOOMGENERIC_RESY);
    free(loading);
    quinn_process(0);
}

void DG_DrawFrame() {
    quinn_render_no_alpha(window_handle, surface, DG_ScreenBuffer, DOOMGENERIC_RESX, DOOMGENERIC_RESY, 0, 0, DOOMGENERIC_RESX, DOOMGENERIC_RESY);
    quinn_process(0);
}

void DG_SleepMs(uint32_t ms) {
    unsigned int ticks;
    unsigned int now = 0;
    __asm__ volatile ("int $0x30" : "=a" (ticks) : "0" (46));

    while (ticks + (ms * 10) > now) {
        __asm__ volatile ("int $0x30" : "=a" (now) : "0" (46));
    }
}

uint32_t DG_GetTicksMs() {
    unsigned int ticks;
    __asm__ volatile ("int $0x30" : "=a" (ticks) : "0" (46));
    return (ticks - start_ticks) * 10;
}

int DG_GetKey(int *pressed, unsigned char *key) {
    if (s_KeyQueueReadIndex == s_KeyQueueWriteIndex)
    {
        //key queue is empty

        return 0;
    }
    else
    {
        unsigned short keyData = s_KeyQueue[s_KeyQueueReadIndex];
        s_KeyQueueReadIndex++;
        s_KeyQueueReadIndex %= KEYQUEUE_SIZE;

        *pressed = keyData >> 8;
        *key = keyData & 0xFF;

        return 1;
    }
}

void DG_SetWindowTitle(const char *title) {
    quinn_set_win_caption(window_handle, title);
}
