#include "../termbox.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static int curCol = 0;
static int curRune = 0;
static struct tb_cell *backbuf;
static int bbw = 0, bbh = 0;

static const uint32_t runes[] = {
	0x20, // ' '
	0x2591, // '░'
	0x2592, // '▒'
	0x2593, // '▓'
	0x2588, // '█'
};

#define len(a) (sizeof(a)/sizeof(a[0]))

static const uint16_t colors[] = {
	TB_BLACK,
	TB_RED,
	TB_GREEN,
	TB_YELLOW,
	TB_BLUE,
	TB_MAGENTA,
	TB_CYAN,
	TB_WHITE,
};

void updateAndDrawButtons(int *current, int x, int y, int mx, int my, int n, void (*attrFunc)(int, uint32_t*, uint16_t*, uint16_t*)) {
	int lx = x;
	int ly = y;
	for (int i = 0; i < n; i++) {
		if (lx <= mx && mx <= lx+3 && ly <= my && my <= ly+1) {
			*current = i;
		}
		uint32_t r;
		uint16_t fg, bg;
		(*attrFunc)(i, &r, &fg, &bg);
                tb_change_cell(lx+0, ly+0, r, fg, bg);
                tb_change_cell(lx+1, ly+0, r, fg, bg);
                tb_change_cell(lx+2, ly+0, r, fg, bg);
                tb_change_cell(lx+3, ly+0, r, fg, bg);
                tb_change_cell(lx+0, ly+1, r, fg, bg);
                tb_change_cell(lx+1, ly+1, r, fg, bg);
                tb_change_cell(lx+2, ly+1, r, fg, bg);
                tb_change_cell(lx+3, ly+1, r, fg, bg);
                lx += 4;
	}
	lx = x;
	ly = y;
        for (int i = 0; i < n; i++) {
                if (*current == i) {
                        uint16_t fg = TB_RED | TB_BOLD;
                        uint16_t bg = TB_DEFAULT;
                        tb_change_cell(lx+0, ly+2, '^', fg, bg);
                        tb_change_cell(lx+1, ly+2, '^', fg, bg);
                        tb_change_cell(lx+2, ly+2, '^', fg, bg);
                        tb_change_cell(lx+3, ly+2, '^', fg, bg);
                }
                lx += 4;
        }
}

void runeAttrFunc(int i, uint32_t *r, uint16_t *fg, uint16_t *bg) {
	*r = runes[i];
	*fg = TB_DEFAULT;
	*bg = TB_DEFAULT;
}

void colorAttrFunc(int i, uint32_t *r, uint16_t *fg, uint16_t *bg) {
	*r = ' ';
	*fg = TB_DEFAULT;
	*bg = colors[i];
}

void updateAndRedrawAll(int mx, int my) {
	tb_clear();
	if (mx != -1 && my != -1) {
		backbuf[bbw*my+mx].ch = runes[curRune];
		backbuf[bbw*my+mx].fg = colors[curCol];
	}
	memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw*bbh);
	int h = tb_height();
	updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc);
	updateAndDrawButtons(&curCol, 0, h-3, mx, my, len(colors), colorAttrFunc);
	tb_present();
}

void reallocBackBuffer(int w, int h) {
	bbw = w;
	bbh = h;
	if (backbuf)
		free(backbuf);
	backbuf = calloc(sizeof(struct tb_cell), w*h);
}

int main(int argv, char **argc) {
	(void)argc; (void)argv;
	int code = tb_init();
	if (code < 0) {
		fprintf(stderr, "termbox init failed, code: %d\n", code);
		return -1;
	}

	tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
	int w = tb_width();
	int h = tb_height();
	reallocBackBuffer(w, h);
	updateAndRedrawAll(-1, -1);
	for (;;) {
		struct tb_event ev;
		int mx = -1;
		int my = -1;
		int t = tb_poll_event(&ev);
		if (t == -1) {
			tb_shutdown();
			fprintf(stderr, "termbox poll event error\n");
			return -1;
		}

		switch (t) {
		case TB_EVENT_KEY:
			if (ev.key == TB_KEY_ESC) {
				tb_shutdown();
				return 0;
			}
			break;
		case TB_EVENT_MOUSE:
			if (ev.key == TB_KEY_MOUSE_LEFT) {
				mx = ev.x;
				my = ev.y;
			}
			break;
		case TB_EVENT_RESIZE:
			reallocBackBuffer(ev.w, ev.h);
			break;
		}
		updateAndRedrawAll(mx, my);
	}
}
