/*
 *	Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *	SOFTWARE.
 */


#include "ui.h"
#include "portme.h"
#include "lib.h"
#include "state.h"
#include "opcodes.h"
#include "story.h"
#include "interpreter.h"
#include "messages.h"
#include "text.h"


#define uiStringPrintAt(x,y,s)  portCharSetPos(x, y);  uiStringPrint(s)


typedef struct fileListS {
	char             *filename;
	byte              filenameLen;
	uint32_t          filesize;
	struct fileListS *next;
} fileListT;


static void uiBox(byte x, byte y, byte w, byte h);
static void uiCenter(char *m);
static void uiDialog(byte fc, byte bc, byte tc, byte y, char *message);
static void uiStringPrint(char *s);
static void uiTitleDraw(void);


static byte       _screenHeight;
static byte       _screenWidth;
static char      *_saveName      = 0;
static fileListT *_games         = 0;
static byte       _longestName   = 0;
static fileListT *_selected      = 0;
static uint16_t   _gameCount     = 0;


static void uiBox(byte x, byte y, byte w, byte h) {
	byte  i;
	byte  j;
	char *b = portSymbolsGet();

	portCharSetPos(x, y);
	portCharPrint(b[0]);
	for (j=0; j<w-2; j++) portCharPrint(b[1]);
	portCharPrint(b[2]);

	for (i=y+1; i<y+h-1; i++) {
		portCharSetPos(x, i);
		portCharPrint(b[7]);
		for (j=0; j<w-2; j++) portCharPrint(' ');
		portCharPrint(b[3]);
	}

	portCharSetPos(x, y+h-1);
	portCharPrint(b[6]);
	for (j=0; j<w-2; j++) portCharPrint(b[5]);
	portCharPrint(b[4]);
}


static void uiCenter(char *m) {
	byte x;
	byte y;
	byte w = 80;
	byte l = libStrLen(m);

	portCharGetPos(&x, &y);
	portCharSetPos((w - l) / 2, y);
	for (x=0; x<l; x++) portCharPrint(m[x]);
	portCharSetPos(1, y + 1);
}


static void uiDialog(byte fc, byte bc, byte tc, byte y, char *message) {
	byte l;
	byte x;

	l = libStrLen(message);
	x = (_screenWidth - l) / 2 - 2;
	portColorSet(fc, bc);
	uiBox(x - 1, y, l + 4, 5);
	portColorSet(tc, bc);
	uiStringPrintAt(x + 1, y + 2, message);
}


void uiFileAdd(char *filename, uint32_t size) {
	uint32_t   l;
	byte       v;
	fileListT *newGame;
	fileListT *currentGame;

	l = libStrLen(filename);

	// Right now, we only support V1-V3 games.  Filter the rest.
	if (l > 3) {
		if ((filename[l - 2] == 'z') || (filename[l - 2] == 'Z')) {
			if (filename[l - 3] == '.') {
				v = filename[l - 1] - '0';
				if (v <= 3) {
					// Add game to chooser.

					// Create new game entry.
					newGame = (fileListT *)portMalloc(sizeof(fileListT));
					if (newGame == 0) portDie("M1"); // portDie(MSG_UI_CANNOT_ALLOCATE, sizeof(fileListT));
					newGame->filename = (char *)portMalloc(l + 1);
					if (newGame->filename == 0) portDie("M2"); // portDie(MSG_UI_CANNOT_ALLOCATE, l + 1);
					for (v=0; v<l+1; v++) newGame->filename[v] = filename[v];
					newGame->filesize = size;
					newGame->filenameLen = l;
					newGame->next = 0;

					// Insert into list, sorted by filename.
					if ((_games == 0) || (libStrICmp(_games->filename, newGame->filename) >= 0)) {
						newGame->next = _games;
						_games = newGame;
					} else {
						currentGame = _games;
						while ((currentGame->next != 0) && (libStrICmp(currentGame->next->filename, newGame->filename) < 0)) {
							currentGame = currentGame->next;
						}
						newGame->next = currentGame->next;
						currentGame->next = newGame;
					}
				}
			}
		}
	}
}


void uiGameSelect(char *path) {
	byte       x;
	byte       y;
	byte       h;
	byte       w;
	byte       l;
	uint16_t   i;
	fileListT *g;
	fileListT *t;
	byte       c;
	uint16_t   top;
	uint16_t   selector;

	// ***TODO*** This doesn't check if filenames are too long to fit on the screen.

	portCursorShow(false);
	uiTitleDraw();
	portCharGetPos(&x, &y);
	y += 2;

	portFileLister(path);

	// No games?
	if (_games == 0) {
		uiDialog(15, 4, 14, y, "  No story files found!  ");
		portCharGet();
		return;
	}

	// Count games and find longest.
	g = _games;
	_gameCount = 0;
	while (g) {
		_gameCount++;
		if (g->filenameLen > _longestName) _longestName = g->filenameLen;
		g = g->next;
	}

	// Draw chooser window.
	x = (_screenWidth - _longestName - 4) / 2;
	w = _longestName + 4;
	h = _screenHeight - y;
	portColorSet(15, 1);
	uiBox(x, y, w, h);

	// Adjust for future drawing.
	x++;
	y++;
	h -= 2;
	w -= 2;

	// Run chooser.
	top = 0;
	selector = 0;
	do {
		g = _games;
		// Skip games that have scrolled off the top.
		for (i=0; i<top; i++) {
			g = g->next;
		}
		// Draw list and selector.
		for (i=0; i<h && g != 0; i++) {
			if (i == selector) {
				portColorSet(1, 7);
				_selected = g;
			} else {
				portColorSet(7, 1);
			}
			portCharSetPos(x, y + i);
			portCharPrint(' ');
			uiStringPrint(g->filename);
			for (c=g->filenameLen+1; c<w; c++) portCharPrint(' ');
			g = g->next;
		}
		// Get input.
		c = portCharGet();
		if (c == UI_KEY_DOWN) {
			if ((selector < h-1) && (selector < _gameCount-1)) {
				selector++;
			} else {
				if (top + h < _gameCount) {
					top++;
				}
			}
		}
		if (c == UI_KEY_UP) {
			if (selector > 0) {
				selector--;
			} else {
				if (top > 0) {
					top--;
				}
			}
		}
	} while (c != 13);

	// Loading dialog.
	uiDialog(15, 8, 15, y + 3, "  Loading story!  ");

	// Dump game list, except selected game.
	g = _games;
	while (g) {
		if (g == _selected) {
			g = g->next;
		} else {
			portFree(g->filename);
			t = g;
			g = g->next;
			portFree(t);
		}
	}

	// Generate full story name (destroys x & y from above).
	x = 0;
	y = libStrLen(path);
	for (i=0; i<y; i++) __memoryBuffer[x++] = path[i];
	for (i=0; i<_selected->filenameLen; i++) __memoryBuffer[x++] = _selected->filename[i];
	__memoryBuffer[x] = 0;

	// Generate save game name (relies on x and __memoryBuffer from above).
	_saveName = (char *)portMalloc(x + 2);
	if (_saveName == 0) portDie(MSG_UI_CANNOT_ALLOCATE, x + 2);
	for (i=0; i<x; i++) _saveName[i] = __memoryBuffer[i];
	_saveName[x - 2] = 's';
	_saveName[x - 1] = 'a';
	_saveName[x    ] = 'v';
	_saveName[x + 1] = 0;


/*
	uiStringPrint("\nstory = [");
	uiStringPrint(__memoryBuffer);
	uiStringPrint("]");
	uiStringPrint("\nsave = [");
	uiStringPrint(_saveName);
	uiStringPrint("]\n");
	portCharGet();
*/


	// Get ready (relies on __memoryBuffer from above).
	stateStartup();
	stateReset();
	portStoryLoad(__memoryBuffer, &_selected->filesize);
	storyChecksumCalculate();
	opcodesSetup();
	storySetup();

	// Set up display.
	portAttributeSet(TEXT_ATTR_NORMAL);
	portScreenClear();
	__state.savedX = 1;
	__state.savedY = storyScreenHeightLines();
	portCharSetPos(__state.savedX, __state.savedY);
	portCursorShow(true);

	// Start the game!
	interpreterRun();

	// Shutdown.
	stateShutdown();

	// Free selected game info.
	portFree(_selected->filename);
	portFree(_selected);
	portFree(_saveName);
}


char *uiSaveGet(void) {
	return _saveName;
}


void uiSizeGet(byte *w, byte *h) {
	*w = _screenWidth;
	*h = _screenHeight;
}


void uiSizeSet(byte w, byte h) {
	_screenWidth  = w;
	_screenHeight = h;
}


char *uiStoryGet(void) {
	return _selected->filename;
}


uint32_t uiStorySizeGet(void) {
	return _selected->filesize;
}


static void uiStringPrint(char *s) {
	byte i = 0;
	while (s[i] != 0) portCharPrint(s[i++]);
}


static void uiTitleDraw(void) {

	portColorSet(9, 0);
	portScreenClear();
	portCharSetPos(1, 1);

	uiCenter("  Welcome to...                  ");
	portColorSet(11, 0);
	uiCenter(" __  __           _     _ _      ");
	uiCenter("|  \\/  |         | |   | | |     ");
	uiCenter("| \\  / |_   _  __| | __| | | ___ ");
	uiCenter("| |\\/| | | | |/ _` |/ _` | |/ _ \\");
	uiCenter("| |  | | |_| | (_| | (_| | |  __/");
	uiCenter("|_|  |_|\\__,_|\\__,_|\\__,_|_|\\___|");
	uiCenter("");
	portColorSet(9, 0);
	uiCenter("             Release 1.0 Beta 3  ");
	uiCenter("");
	portColorSet(12, 0);
	uiCenter("Copyright 2024 Scott Duensing, All Rights Reserved");
	portColorSet(4, 0);
	uiCenter("2024 Kangaroo Punch Studios  https://kangaroopunch.com");
}
