
#include "menu.h"
#include "announce.h"
#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "gfx.h"
#include "network.h"
#include "sound.h"
#include "utils.h"

GameOptions configopts = {2, 1, 0, 2};

/* Generic menu */

typedef enum { MENU_ANY = -1, MENU_MAIN = 0, MENU_CONFIG = 2, MENU_JOIN = 3 } menuname;

#define MENU_SELECT(x) ((menuname) (-x - 1))

static int menuhistory[32] = {0};
static char menustring[1024];
static char *menuput, *menuitems[40], *menutitle;
static int menunum, menuexit;
static int menudelta;

static void drawmenu(int selected) {
	int i, j;
	int tx, ty;

	clear();
	j = strlen(menutitle) * bigfontxsize;
	drawbigstring((IXSIZE - j) >> 1, 20, menutitle);
	ty = ((IYSIZE - (bigfontysize * menunum)) >> 1) - (IYSIZE >> 3);
	for (i = 0; i < menunum; ++i) {
		j = strlen(menuitems[i]) * bigfontxsize;
		tx = (IXSIZE - j) >> 1;
		if (i == selected) {
			greyrect(0, ty - 1, tx - 5, bigfontysize);
			greyrect(tx + j + 3, ty - 1, IXSIZE - (tx + j + 3), bigfontysize);
		}
		drawbigstring(tx, ty, menuitems[i]);
		ty += bigfontyspace;
	}
}

static int domenu(menuname whichmenu, int (*pause)(void)) {
	char redraw;
	int selected;
	int mcount = 0;

	if (whichmenu >= 0)
		selected = menuhistory[whichmenu];
	else
		selected = -whichmenu - 1;

	if (!pause) pause = mypause;

	redraw = 1;
	clearspritelist();
	while (!exitflag) {
		if (redraw) {
			drawmenu(selected);
			redraw = 0;
		}
		if (!pause()) return -1;
		scaninput();
		++mcount;

		clearsprites();
		clearspritelist();
		addsprite(IXSIZE / 2 - 50 - 20, IYSIZE - 80, walking[2] + (30 + mcount % 15));
		addsprite(IXSIZE / 2 + 50 - 20, IYSIZE - 80, walking[3] + (30 + (mcount + 7) % 15));
		plotsprites();
		copyup();

		if (anydown()) playsound(3);
		while (anydown()) {
			switch (takedown()) {
			case MYLEFT:
				menudelta = -1;
				return selected;
			case MYRIGHT:
			case ' ':
			case 13:
				menudelta = 1;
				return selected;
			case 'k':
			case MYUP:
				if (selected)
					--selected;
				else
					selected = menunum - 1;
				if (whichmenu >= 0) menuhistory[whichmenu] = selected;
				redraw = 1;
				break;
			case 'j':
			case MYDOWN:
				++selected;
				if (selected == menunum) selected = 0;
				if (whichmenu >= 0) menuhistory[whichmenu] = selected;
				redraw = 1;
				break;
			case 0x1b:
				if (MENU_MAIN == whichmenu && menuexit != selected) {
					selected = menuexit;
					redraw = 1;
					break;
				}
				menudelta = 1;
				return menuexit;
			}
		}
	}
	menudelta = 0;
	return menuexit;
}

static void menustart() {
	menunum = -1;
	menuput = menustring;
	*menuput = 0;
	menuexit = 0;
}

static void additem_s(char* item, int len) {
	if (len < 0 || (menustring + sizeof(menustring) - menuput <= len)) return;

	if (menunum < 0)
		menutitle = menuput;
	else
		menuitems[menunum] = menuput;
	++menunum;
	memcpy(menuput, item, len + 1);
	menuput += len + 1;
}

static void additem(char* item, ...) {
	char output[256];
	va_list ap;
	int len;

	va_start(ap, item);

	len = vsnprintf(output, sizeof(output), item, ap);
	if (len >= 256) len = 255; /* truncated string */

	additem_s(output, len);
}

static void addexit(char* item, ...) {
	char output[256];
	va_list ap;
	int len;

	va_start(ap, item);

	len = vsnprintf(output, sizeof(output), item, ap);
	if (len >= 256) len = 255; /* truncated string */

	menuexit = menunum;
	additem_s(output, len);
}


/* end generic menu */

/* game menues */

static const char* densities[] = {"PACKED", "HIGH", "MEDIUM", "LOW"};
static const char* generosities[] = {"LOW", "MEDIUM", "HIGH", "RIDICULOUS"};
static const char* dis_en_abled[] = {"DISABLED", "ENABLED"};

static void config_menu(void) {
	int sel;

	while (!exitflag) {
		menustart();
		additem("GAME OPTIONS");
		additem("RETURN TO MAIN MENU");
		additem("DENSITY: %s", densities[configopts.density]);
		additem("GENEROSITY: %s", generosities[configopts.generosity]);
		additem("INITIAL FLAME LENGTH: %d", configopts.flames + 1);
		additem("INITIAL NUMBER OF BOMBS: %d", configopts.bombs + 1);
		additem("SOUND: %s", dis_en_abled[sound_enabled]);
		sel = domenu(MENU_CONFIG, NULL);
		switch (sel) {
		case 0:
			return;
		case 1:
			configopts.density += menudelta;
			configopts.density &= 3;
			break;
		case 2:
			configopts.generosity += menudelta;
			configopts.generosity &= 3;
			break;
		case 3:
			configopts.flames += menudelta;
			configopts.flames &= 7;
			break;
		case 4:
			configopts.bombs += menudelta;
			configopts.bombs &= 7;
			break;
		case 5:
			sound_enabled = 1 - sound_enabled;
			break;
		}
	}
}

static void draw_host_game(void) {
	int i;
	char* name;
	char temp[64];

#define M3X (IXSIZE / 3)
#define M3Y (IYSIZE / 4)

	clear();
	centerbig(20, "HOST NETWORK GAME");
	drawbigstring(M3X, M3Y, "SLOT    NAME");
	for (i = 0; i < MAXNETNODES; ++i) {
		if (!netnodes[i].used) continue;
		name = netnodes[i].name;
		snprintf(temp, sizeof(temp), "  %d     %s", i + 1, name);
		drawbigstring(M3X, M3Y + (i + 2) * bigfontyspace, temp);
	}

	copyup();
}


static void host_game(void) {
	create_seed_unique();
	if (!start_network_game()) {
		failure("COULD NOT REGISTER GAME");
		return;
	}

	draw_host_game();
	for (;;) {
		scaninput();
		while (anydown()) {
			switch (takedown()) {
			case 0x1b:
				unregistergame();
				cancel_network_game();
				return;
			case ' ':
			case 13:
				unregistergame();
				if (begin_network_game()) {
					run_network_game();
					return;
				}
				send_invites();
				draw_host_game();
			}
		}

		if (!handle_joins()) continue;

		send_invites();
		draw_host_game();
	}
}

static void drawjoinscreen(void) {
	int i;
	char name[17];
	char temp[64];

#define JX (IXSIZE / 3)
#define JY (IYSIZE / 4)

	clear();
	centerbig(20, "JOIN NETWORK GAME");
	drawbigstring(JX, JY, "SLOT    NAME");
	for (i = 0; i < MAXNETNODES; ++i) {
		if (!netnodes[i].used) continue;
		memmove(name, netnodes[i].name, 16);
		name[16] = 0;
		snprintf(temp, sizeof(temp), "  %d     %s", i + 1, name);
		drawbigstring(JX, JY + (i + 1) * bigfontyspace, temp);
	}
}

static int tryjoin(int which) {
	int res;

	if (0 == (res = send_join(&gamelistentries[which].netname, playername))) { return 0; }

	while (!exitflag) {
		switch (res) {
		case 1:
			failure("Game closed");
			return 0;
		case 2:
			drawjoinscreen();
			copyup();
			break;
		case 3:
			return 1;
		default:
			break;
		}
		scaninput();
		while (anydown()) {
			switch (takedown()) {
			case 0x1b:
				send_quit();
				return 0;
			}
		}
		res = scaninvite(200);
	}
	return 0;
}

static int join_game_pause(void) {
	if (find_more_games()) return 0;
	mypause();
	return 1;
}

static void join_game(void) {
	int i;
	int sel = -1;

	if (!searchgames()) { return; }

	menuhistory[MENU_JOIN] = 0;

	while (-1 == sel) {
		menustart();
		if (gamelistsize == 0) {
			additem("JOIN NETWORK GAME - NO GAMES AVAILABLE");
			addexit("EXIT");
		} else {
			additem("JOIN NETWORK GAME");
			for (i = 0; i < gamelistsize; i++) { additem(gamelistentries[i].name); }
			addexit("EXIT");
		}
		sel = domenu(MENU_JOIN, join_game_pause);
		if (menudelta < 0) sel = -1;
	}
	stop_search();

	if (menuexit == sel || !gamelistsize) { return; }
	if (!tryjoin(sel)) { return; }
	run_network_game();
}

void mainloop(void) {
	int sel;

	exitflag = 0;
	while (!exitflag) {
		menustart();
		additem("BOMBER MAIN MENU");
		additem("EXIT GAME");
		additem("START SINGLE PLAYER GAME");
		additem("OPTIONS");
		// 		additem("REMAP MOVEMENT KEYS");
		additem("START NETWORK GAME");
		additem("JOIN NETWORK GAME");
		sel = domenu(MENU_MAIN, NULL);
		if (menudelta < 0) sel = -1;
		switch (sel) {
		case 0:
			return;
		case 1:
			run_single_player();
			break;
		case 2:
			config_menu();
			break;
		case 3:
			host_game();
			break;
		case 4:
			join_game();
			break;
		}
	}
}
