/*
 * Kangaroo Punch MultiPlayer Game Server Mark II
 * Copyright (C) 2020-2021  Scott Duensing
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */


/*
 * To Do:
 *
 * - Replace any direct data manipulation from outside a class with methods to handle it
 * - More widget states:  Ghosted (underway)
 * - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well
 * - Metrics, colors, etc. should be defined in each widget and not in GUI
 * - Widgets should support a "changed" callback that can cancel the change
 * - Use LabelT in all widgets that have a label
 * - Find a light grey to replace white widget data areas
 * - No thumb in listbox scrollbar
 * - Layout container widgets!
 * - Fix variable names.  something = local; _something = file global; __something = project global
 */


#include "stddclmr.h"
#include "os.h"
#include "vesa.h"
#include "surface.h"
#include "mouse.h"
#include "keyboard.h"
#include "image.h"
#include "font.h"
#include "gui.h"
#include "config.h"
#include "runtime.h"
#include "comport.h"
#include "network.h"
#include "timer.h"

#include "welcome.h"
#include "settings.h"


PacketThreadDataT *__packetThreadData = NULL;  // Exported in os.h
RuntimeDataT       __runtimeData;              // Exported in runtime.h

static MouseT *_mouse   = NULL;
static ImageT *_pointer = NULL;
static ColorT  _alpha   = 0;

#ifndef __linux__
char *_logName = NULL;
#endif


static void    checkSettings(void);
static void    eventLoop(void);
static uint8_t hasValidSettings(void);
static void    shutdown(void);
static uint8_t startup(int argc, char *argv[]);
static void    tableLoad(void);
static void    tableSave(void);


static void checkSettings(void) {
	// Do we have a valid COM port?
	if (!hasValidSettings()) {
		logWrite("No compatible modem found.  Cannot continue.\n");
		guiStop();
		return;
	}

	welcomeShow();
	eventLoop();
}


static void eventLoop(void) {
	// Main Event Loop.
	do {
		netProcess();
		guiTimerProcess(rawclock());
		_mouse = mouseRead();
		if (keyHit()) {
			guiKeyboardProcess(keyASCIIGet(), keyExtendedGet(), keyScanCodeGet(), keyShiftGet(), keyControlGet(), keyAltGet());
			//logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", keyASCIIGet(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt());
			if (keyASCIIGet() == 27) guiStop();
		}
		guiMouseProcess(_mouse);
		guiComposite();
		imageRenderWithAlpha(_pointer, _mouse->x, _mouse->y, _alpha);
		vbeVBlankWait();
		vbePresent();
	} while (!guiHasStopped());

	shutdown();
}


static uint8_t hasValidSettings(void) {
	// Returning false sends us immediately to the settings dialog.
	return (__configData.serialCom > 0 && __configData.serialCom < 5);
}


static void shutdown(void) {
#ifndef __linux__
	// On DOS, display the contets of the log now that we're back in text mode.
	FILE *in      = NULL;
#endif

	imageUnload(&_pointer);

	tableSave();

	netShutdown();
	guiShutdown();
	mouseShutdown();
	surfaceShutdown();
	vbeShutdown();
	cacheShutdown();
	configShutdown();
	osShutdown();
	logClose();
	memoryShutdown();

#ifndef __linux__
	//***TODO*** Why the frack does this not work?!
	textmode(C80);
	// On DOS, display the contets of the log now that we're back in text mode.
	in = fopen(_logName, "r");
	if (in) {
		while (!feof(in)) {
			putc(fgetc(in), stdout);
		}
		fclose(in);
		free(in);
	}
	free(_logName);
	_Exit(0);
#endif
}


static uint8_t startup(int argc, char *argv[]) {
	memoryStartup(argv[0]);
	logOpenByHandle(memoryLogHandleGet());
	osStartup();
	configStartup(argv[0]);

#ifndef __linux__
	_logName = utilAppNameWithNewExtensionGet(argv[0], "log");
#endif

	//      0        1         2         3         4         5         6         7         8
	//      12345678901234567890123456789012345678901234567890123456789012345678901234567890
	logWrite("%s", "Kangaroo Punch MultiPlayer DOS Game Client Mark II\n");
	logWrite("%s", "Copyright (C) 2020-2021  Scott Duensing  scott@kangaroopunch.com\n\n");

	if (argc > 1) {
		if (strcmp(argv[1], "/?") == 0) {
			vbeInfoShow();
			configShutdown();
			logClose();
			memoryShutdown();
			return 1;
		}
	}

	// Do we have the video mode they asked for?
	if (vbeStartup(__configData.videoWidth, __configData.videoHeight, __configData.videoDepth)) {
		configShutdown();
		logClose();
		memoryShutdown();
		return 1;
	}

	cacheStartup(argv[0]);
	surfaceStartup();
	mouseStartup();
	guiStartup();
	netStartup();

	_pointer = imageLoad(cacheFilenameGet("system:mouse"));
	_alpha   = imagePixelGet(_pointer, 5, 0);

	tableLoad();

	return 0;
}


static void tableLoad(void) {
	FILE *cache = NULL;
	char *line  = NULL;
	char *p     = NULL;
	char *temp  = NULL;

	__runtimeData.integers        = NULL;
	__runtimeData.strings         = NULL;
	__runtimeData.protocolVersion = 0;

	sh_new_strdup(__runtimeData.integers);
	sh_new_strdup(__runtimeData.strings);

	// ***TODO*** Default initial tables

	line = (char *)malloc(4096);
	if (line) {
		// Load string cache.
		cache = cacheFOpen("data:strings.dat", "rt");
		if (cache) {
			while (fscanf(cache, "%s\n", line) != EOF) {
				p = strstr(line, "=");
				if (p) {
					*p = 0;
					p++;
					// Do we have this string already?
					temp = shget(__runtimeData.strings, line);
					if (temp) {
						DEL(temp);
						shdel(__runtimeData.strings, line);
					}
					shput(__runtimeData.strings, line, strdup(p));
				}
			}
			cacheFClose(cache);
		}
		// Load integer cache.
		cache = cacheFOpen("data:integers.dat", "rt");
		if (cache) {
			while (fscanf(cache, "%s\n", line) != EOF) {
				p = strstr(line, "=");
				if (p) {
					*p = 0;
					p++;
					shput(__runtimeData.integers, line, atol(p));
				}
			}
			cacheFClose(cache);
		}
		free(line);
		line = NULL;
	}

}


static void tableSave(void) {
	FILE *cache = NULL;

	// Save & free integer table.
	cache = cacheFOpen("data:integers.dat", "wt");
	if (cache) {
		if (__runtimeData.integers) {
			while (shlen(__runtimeData.integers) > 0) {
				//logWrite("[%s]=[%d]\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value);
				fprintf(cache, "%s=%ld\n", __runtimeData.integers[0].key, (long)__runtimeData.integers[0].value);
				shdel(__runtimeData.integers, __runtimeData.integers[0].key);
			}
			shfree(__runtimeData.integers);
		}
		cacheFClose(cache);
	}

	// Save & free string table.
	cache = cacheFOpen("data:strings.dat", "wt");
	if (cache) {
		if (__runtimeData.strings) {
			while (shlen(__runtimeData.strings) > 0) {
				//logWrite("[%s]=[%s]\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
				fprintf(cache, "%s=%s\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
				DEL(__runtimeData.strings[0].value);
				shdel(__runtimeData.strings, __runtimeData.strings[0].key);
			}
			shfree(__runtimeData.strings);
		}
		cacheFClose(cache);
	}
}


int main(int argc, char *argv[]) {

	if (startup(argc, argv)) return 1;

	// Perform "first run" setup tasks or start the client?
	if (hasValidSettings()) {
		// We have what we need, start the client.
		welcomeShow();
	} else {
		// Run the setup.
		settingsShow(checkSettings);
	}

	eventLoop();

	return 0;
}
