#include "Menus.h"

#include <SDL.h>

#include "Audio.h"
#include "Game.h"
#include "Gui.h"
#include "Main.h"
#include "Render.h"
#include "Version.h"
#include "Video.h"
#include "World.h"

Bmp_t titleLogo;

static struct GuiWidget titleWidgets[] = {
	{.type = WIDGET_IMAGE,  .data = &titleLogo },
	{.type = WIDGET_BUTTON, .data = "Continue Game" },
	{.type = WIDGET_BUTTON, .data = "Start New Game" },
	{.type = WIDGET_BUTTON, .data = "Host Multiplayer" },
	{.type = WIDGET_BUTTON, .data = "Join Multiplayer" },
	{.type = WIDGET_BUTTON, .data = "Options" },
	{.type = WIDGET_BUTTON, .data = "Quit to Desktop" },
};

static void HandleTitleClick(int index)
{
	switch (index)
	{
	case 1:
		Game_Start(GAME_START_CONTINUE, GAME_START_FLOOR);
		break;
	case 2:
		Game_Start(GAME_START_NEW, GAME_START_FLOOR);
		break;
	case 3:
		Game_Start(GAME_START_HOST, GAME_START_FLOOR);
		break;
	case 4:
		Game_Start(GAME_START_JOIN, GAME_START_FLOOR);
		break;
	case 5:
		Game_SetMenu(&Menu_Options);
		break;
	case 6:
		Main_QuitGame();
		break;
	default:
		break;
	}
}

struct GuiMenu Menu_TitleScreen = {
	.widgets = titleWidgets,
	.numWidgets = 7,
	.bgType = GUIBG_NONE,
	.drawProc = Gui_DrawMenu,
	.layoutProc = Gui_LayoutMenu,
	.clickProc = HandleTitleClick
};

static struct GuiWidget pauseWidgets[] = {
	{.type = WIDGET_TEXT,   .data = "--- Paused ---" },
	{.type = WIDGET_BUTTON, .data = "Resume Game" },
	{.type = WIDGET_BUTTON, .data = "Start New Game" },
	{.type = WIDGET_BUTTON, .data = "Options" },
	{.type = WIDGET_BUTTON, .data = "Save & Quit" },
};

static void HandlePauseClick(int index)
{
	switch (index)
	{
	case GUI_CLOSEMENU:
	case 1:
		Game_TogglePaused();
		break;
	case 2:
		Game_Start(GAME_START_NEW, GAME_START_FLOOR);
		break;
	case 3:
		Game_SetMenu(&Menu_Options);
		break;
	case 4:
		Game_QuitToTitle();
		break;
	default:
		break;
	}
}

struct GuiMenu Menu_Pause = {
	.widgets = pauseWidgets,
	.numWidgets = 5,
	.bgType = GUIBG_DARKENSCREEN,
	.drawProc = Gui_DrawMenu,
	.layoutProc = Gui_LayoutMenu,
	.clickProc = HandlePauseClick
};

static Bmp_t gameOverScreen;

static char deathMenuText[64];
static char deathNewGameText[64];
static struct GuiWidget deadWidgets[] = {
	{.type = WIDGET_IMAGE,  .data = &gameOverScreen },
	{.type = WIDGET_TEXT,   .data = deathMenuText },
	{.type = WIDGET_BUTTON, .data = deathNewGameText },
	{.type = WIDGET_BUTTON, .data = "Quit to Title" },
};

static void LayoutDeathMenu(struct GuiMenu *menu, Bmp_t *screen)
{
	SDL_snprintf(deathMenuText, 64, "Kills: %d\nItems: %d\n\nDamage Dealt: %d\nDamage Taken: %d",
		World_Players[0].deathStatistics.kills, World_Players[0].deathStatistics.items, World_Players[0].deathStatistics.dmgDealt, World_Players[0].deathStatistics.dmgTaken);
	SDL_strlcpy(deathNewGameText, "Start New Game", 64);
	Gui_LayoutMenu(menu, screen);
}

static void HandleDeathClick(int index)
{
	switch (index)
	{
	case 2:
		Game_Start(GAME_START_NEW, GAME_START_FLOOR);
		break;
	case 3:
		Game_QuitToTitle();
		break;
	default:
		break;
	}
}

struct GuiMenu Menu_Dead = {
	.widgets = deadWidgets,
	.numWidgets = 4,
	.bgType = GUIBG_RECT,
	.drawProc = Gui_DrawMenu,
	.layoutProc = LayoutDeathMenu,
	.clickProc = HandleDeathClick
};

#define MOUSESENS_MIN 0.06f
#define MOUSESENS_MAX 2.6f

#define RESOLUTION_NAME_LENGTH 12
static int *resolutions;
static char **resolutionNames;
static int numResolutions = 0;

static char *fullscreenModeNames[] = {
	"Windowed", "Fullscreen (Borderless Window)", "Fullscreen"
};

static float mouseSensPercent;
static char *audioDeviceNames[AUDIO_MAXDEVICES];
static int lastAudioDevice;
static float masterVolPercent, sfxVolPercent, musicVolPercent;

static struct GuiSelectData resolutionData;
static struct GuiSelectData fullscreenModeData = { fullscreenModeNames, 3, 0 };
static struct GuiSelectData audioDeviceData;

void DetectDisplayResolutions(void)
{
	const int displayIndex = 0;
	int currentIndex = 0;
	int lastW = -1, lastH = -1;
	int i, unique = 0;

	numResolutions = SDL_GetNumDisplayModes(displayIndex);
	resolutions = SDL_calloc(numResolutions, sizeof(int) * 2);
	resolutionNames = SDL_calloc(numResolutions, sizeof(char *));

	for (i = 0; i < numResolutions; i++)
	{
		SDL_DisplayMode mode;
		SDL_GetDisplayMode(displayIndex, i, &mode);

		if (lastW == mode.w && lastH == mode.h)
			continue; /* Skip duplicate resolutions, since we ignore refresh rates. */

		if (Game.options.width == mode.w && Game.options.height == mode.h)
			currentIndex = unique;

		resolutions[unique * 2 + 0] = mode.w;
		resolutions[unique * 2 + 1] = mode.h;

		resolutionNames[unique] = SDL_malloc(RESOLUTION_NAME_LENGTH);
		SDL_snprintf(resolutionNames[unique], RESOLUTION_NAME_LENGTH - 1, "%dx%d", mode.w, mode.h);

		lastW = mode.w;
		lastH = mode.h;

		unique++;
	}

	resolutionData.options = resolutionNames;
	resolutionData.numoptions = unique;
	resolutionData.selected = currentIndex;
}

void SetupMenus(const RPKFile *rpak)
{
	DetectDisplayResolutions();

	fullscreenModeData.selected = Game.options.fullscreen ? 1 : 0;

	mouseSensPercent = (Game.options.mouseSens - MOUSESENS_MIN) / (MOUSESENS_MAX - MOUSESENS_MIN);

	audioDeviceData.options = audioDeviceNames;
	audioDeviceData.numoptions = Audio_GetDeviceList(audioDeviceNames);
	lastAudioDevice = audioDeviceData.selected;

	masterVolPercent = Game.options.masterVolume;
	sfxVolPercent = Game.options.sfxVolume;
	musicVolPercent = Game.options.musicVolume;

	gameOverScreen = Bmp_CreateFromPAKEntry(rpak, "GameOver");
	titleLogo = Bmp_CreateFromPAKEntry(rpak, "Logo");
}

void CleanupMenus(void)
{
	int i;

	Bmp_Destroy(&gameOverScreen);
	Bmp_Destroy(&titleLogo);

	for (i = 0; i < numResolutions; i++)
	{
		SDL_free(resolutionNames[i]);
	}
	SDL_free(resolutionNames);
	SDL_free(resolutions);
}

void RelayoutMenus(Bmp_t *screen)
{
	Menu_TitleScreen.layoutProc(&Menu_TitleScreen, screen);
	Menu_Pause.layoutProc(&Menu_Pause, screen);
	Menu_Options.layoutProc(&Menu_Options, screen);
	Menu_Dead.layoutProc(&Menu_Dead, screen);
}

static struct GuiWidget optionsWidgets[] = {
	{.type = WIDGET_TEXT, .data = "--- Options ---"},
	{.type = WIDGET_TEXT, .data = "Graphics"},
	{.type = WIDGET_SELECT, .data = &resolutionData },
	{.type = WIDGET_SELECT, .data = &fullscreenModeData },
	{.type = WIDGET_TEXT, .data = "Input"},
	{.type = WIDGET_SLIDER, .data = &mouseSensPercent },
	{.type = WIDGET_TEXT, .data = "Audio"},
	{.type = WIDGET_SELECT, .data = &audioDeviceData },
	{.type = WIDGET_SLIDER, .data = &masterVolPercent },
	{.type = WIDGET_SLIDER, .data = &sfxVolPercent},
	{.type = WIDGET_SLIDER, .data = &musicVolPercent},
	{.type = WIDGET_BUTTON, .data = "Back" },
	{.type = WIDGET_BUTTON, .data = "Apply" },
};

static const char *controlCaptions[] = {
	"Resolution", "Fullscreen Mode",
	"Mouse Sensitivity",
	"Output Device", "Master Volume", "Sound FX Volume", "Music Volume"
};

void LayoutOptionsMenu(struct GuiMenu *menu, Bmp_t *screen);
void DrawOptionsMenu(struct GuiMenu *menu, Bmp_t *screen);
static void HandleOptionsClick(int index);

struct GuiMenu Menu_Options = {
	.widgets = optionsWidgets,
	.numWidgets = 13,
	.bgType = GUIBG_DARKENSCREEN,
	.drawProc = DrawOptionsMenu,
	.layoutProc = LayoutOptionsMenu,
	.clickProc = HandleOptionsClick
};

static void HandleOptionsClick(int index)
{
	switch (index)
	{
	case GUI_CLOSEMENU:
	case 11: /* Back button */
		Game_SetMenu(Game.state == GAME_STATE_TITLE ? &Menu_TitleScreen : &Menu_Pause);
		break;
	case 12: /* Apply button */
	{
		Game.options.width = resolutions[resolutionData.selected * 2 + 0];
		Game.options.height = resolutions[resolutionData.selected * 2 + 1];
		Game.options.fullscreen = fullscreenModeData.selected;
		Vid_SetDisplayMode(Game.options.width, Game.options.height, Game.options.fullscreen);

		Game.options.mouseSens = MATH_LERP(MOUSESENS_MIN, MOUSESENS_MAX, mouseSensPercent);
		
		if (audioDeviceData.selected != lastAudioDevice)
		{
			Audio_ChangeDevice(audioDeviceData.options[audioDeviceData.selected]);
			lastAudioDevice = audioDeviceData.selected;
		}

		Game.options.masterVolume = masterVolPercent;
		Game.options.sfxVolume = sfxVolPercent;
		Game.options.musicVolume = musicVolPercent;
		Audio_SetVolume(Game.options.masterVolume, Game.options.sfxVolume, Game.options.musicVolume);

		Game_SaveOptions();
		break;
	}
	}
}

void LayoutOptionsMenu(struct GuiMenu *menu, Bmp_t *screen)
{
	int i;
	int hoffs = 120;

	menu->width = 200;
	menu->height = 0;

	Bmp_SetActiveFont(BMPFONT_TINY);

	for (i = 0; i < menu->numWidgets; i++)
	{
		struct GuiWidget *widget = &menu->widgets[i];

		menu->height += 4;

		switch (widget->type)
		{
		case WIDGET_TEXT: /* Category captions, should be left aligned */
			Bmp_TextSize((const char *)widget->data, &widget->w, &widget->h, screen->w);
			widget->x = 0;
			widget->y = menu->height;
			menu->height += 4;
			break;
		case WIDGET_BUTTON:
			if (i == 12)
				menu->height += 4;
			widget->w = 60;
			widget->h = 8;
			widget->x = 8;
			widget->y = menu->height;
			menu->height += 4;
			break;
		case WIDGET_SLIDER:
			widget->w = 100;
			widget->h = 8;
			widget->x = hoffs;
			widget->y = menu->height;
			break;
		case WIDGET_SELECT:
			/* TODO: This should instead use the width of the longest string in the array */
			widget->w = 140;
			widget->h = 10;
			widget->x = hoffs;
			widget->y = menu->height;
			break;
		default:
			break;
		}

		if (widget->w > menu->width)
			menu->width = widget->w;
		if (i == 0)
			menu->height += 6;
		menu->height += widget->h;
	}

	/* Set title caption text to be centered */
	menu->widgets[0].x = (screen->w / menu->widgets[0].w) / 2;

	menu->xOffs = (screen->w - menu->width) / 2;
	menu->yOffs = (screen->h - menu->height) / 2;

	menu->layoutComputed = true;
}

void DrawOptionsMenu(struct GuiMenu *menu, Bmp_t *screen)
{
	/* Left and right starting offsets for the labels and controls respectively */
	// const int headerx = (screen->w / 5);
	// const int leftx = headerx + 8, rightx = (screen->w / 2) + 16;
	// const int sliderw = screen->w - (screen->w / 5) - 8 - rightx;
	int i, controlCounter = 0;

	Gui_DrawMenu(menu, screen);

	Bmp_SetActiveFont(BMPFONT_TINY);
	for (i = 0; i < menu->numWidgets; i++)
	{
		const struct GuiWidget *widget = &menu->widgets[i];
		if (widget->type == WIDGET_TEXT || widget->type == WIDGET_BUTTON)
		{
			continue;
		}
		Bmp_DrawText(screen, controlCaptions[controlCounter++], 0xffdddddd, menu->xOffs + 8, menu->yOffs + widget->y, screen->w);
	}
}
