/*
 * 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/>.
 *
 */


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

#include "taglist.h"
#include "comport.h"
#include "config.h"

#include "window.h"
#include "button.h"
#include "frame.h"
#include "radio.h"
#include "textbox.h"
#include "updown.h"
#include "label.h"
#include "timer.h"


#define GROUP_COM  1

#define PORT_NONE        0
#define PORT_NO_MODEM    1
#define PORT_BAD_MODEM   2
#define PORT_GOOD_MODEM  3

#define TITLE_LEN  80


typedef enum SettingsStateE {
	S_START_SCAN = 0,
	S_OPEN_COM,
	S_WAIT_FOR_INIT,
	S_SCAN_COMPLETE
} SettingsStateT;


typedef struct PortS {
	uint8_t  status;
	char     title[TITLE_LEN];
	uint8_t  selected;
	uint8_t  enabled;
	RadioT  *rdoCOM;
} PortT;


static WindowT        *_winDetecting = NULL;
static LabelT         *_lblOneMoment = NULL;
static WindowT        *_winSettings  = NULL;
static FrameT         *_frmComPorts  = NULL;
static FrameT         *_frmServer    = NULL;
static ButtonT        *_btnOkay      = NULL;
static TextboxT       *_txtServer    = NULL;
static TimerT         *_timProgress  = NULL;
static UpdownT        *_updPort      = NULL;
static PortT           _port[4]      = { 0 };
static widgetCallback  _done         = NULL;
static SettingsStateT  _state        = S_START_SCAN;


static void btnOkayClick(WidgetT *widget);
static void settingsComShow(void);
static void timSettingsProgress(WidgetT *widget);


static void btnOkayClick(WidgetT *widget) {
	uint8_t  x;
	RadioT  *selected = radioSelectedGet(_port[0].rdoCOM);

	(void)widget;

	// Save selected COM port.
	for (x=0; x<4; x++) {
		if (selected == _port[x].rdoCOM) {
			__configData.serialCom = x + 1;
			break;
		}
	}

	// Save server info.
	free(__configData.serverHost);
	__configData.serverHost = strdup(textboxValueGet(_txtServer));
	__configData.serverPort = updownValueGet(_updPort);

	// Return to calling routine.
	guiDelete(D(_winSettings));
	_done(NULL);
}


void settingsShow(void *callback) {

	_done = (widgetCallback)callback;

	TagItemT uiDetecting[] = {
		T_START,
			T_WINDOW, O(_winDetecting),
				T_TITLE, P("Detecting Modems"),
				T_WIDTH, 200, T_HEIGHT, 100,
				T_LABEL, O(_lblOneMoment),
					T_X, 25, T_Y, 25,
					T_TITLE, P("One Moment Please!"),
				T_LABEL, T_DONE,
				T_TIMER, O(_timProgress),
					T_EVENT, P(timSettingsProgress),
					T_VALUE, 0,
				T_TIMER, T_DONE,
			T_WINDOW, T_DONE,
		T_END
	};

	// We can use a module-global variable instead of widget user data
	// because there will never be more than one settings dialog on
	// the screen at a time.
	_state = S_START_SCAN;

	tagListRun(uiDetecting);
}


static void settingsComShow(void) {

	int32_t  y;
	uint32_t len;

	guiDelete(D(_winDetecting));

	TagItemT uiSettings[] = {
		T_START,
			T_WINDOW, O(_winSettings),
				T_TITLE, P("Settings"),
				T_WIDTH, 300, T_HEIGHT, 295,

				T_FRAME, O(_frmComPorts),
					T_X, 10, T_Y, 5, T_WIDTH, 266, T_HEIGHT, 100,
					T_TITLE, P("COM Ports"),
				T_FRAME, T_DONE,

				T_FRAME, O(_frmServer),
					T_X, 10, T_Y, 110, T_WIDTH, 266, T_HEIGHT, 100,
					T_TITLE, P("Server"),
					T_TEXTBOX, O(_txtServer),
						T_X, 5, T_WIDTH, 250,
						T_TITLE, P("Address:"),
						T_VALUE, P(__configData.serverHost),
					T_TEXTBOX, T_DONE,
					T_UPDOWN, O(_updPort),
						T_X, 5, T_Y, 30, T_WIDTH, 250,
						T_TITLE, P("   Port:"),
						T_VALUE, __configData.serverPort,
						T_MINIMUM, 1,
						T_MAXIMUM, 65535,
					T_UPDOWN, T_DONE,
				T_FRAME, T_DONE,

				T_BUTTON, O(_btnOkay),
					T_X, 225, T_Y, 225,
					T_TITLE, P("Okay"),
					T_CLICK, P(btnOkayClick),
				T_BUTTON, T_DONE,

			T_WINDOW, T_DONE,
		T_END
	};

	tagListRun(uiSettings);

	// If we found more than one, use the last selected.
	if (__configData.serialCom > 0 && __configData.serialCom < 4) {
		if (_port[__configData.serialCom - 1].enabled) {
			for (len=0; len<4; len++) {
				_port[len].selected = 0;
			}
			_port[__configData.serialCom - 1].selected = 1;
		}
	}

	// Add COM discovery to GUI.
	y = 0;
	for (len=0; len<4; len++) {
		_port[len].rdoCOM = radioNew(5, y, _port[len].title, GROUP_COM);
		if (_port[len].selected) radioSelectedSet(_port[len].rdoCOM);
		widgetEnableSet(W(_port[len].rdoCOM), _port[len].enabled);
		guiAttach(W(_frmComPorts), W(_port[len].rdoCOM));
		y += 20;
	}
}


static void timSettingsProgress(WidgetT *widget) {
	char     buffer[1024];
	int32_t  rc;
	uint32_t len;

	static uint8_t  selected;
	static int32_t  com;

	switch (_state) {
		case S_START_SCAN:
			com       =  0;
			selected  =  1;
			_state = S_OPEN_COM;
			break;

		case S_OPEN_COM:
			rc = comOpen(com, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS);
			if (rc == SER_SUCCESS) {
				snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13);
				comWrite(com, buffer, strlen(buffer));
				timerQuarterSecondsSet((TimerT *)widget, 4);
				_state = S_WAIT_FOR_INIT;
			} else {
				snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Not Present", com + 1);
				_port[com].status   = PORT_NONE;
				_port[com].selected = 0;
				_port[com].enabled  = 0;
				com++;
				if (com < 4) {
					_state = S_OPEN_COM;
				} else {
					_state = S_SCAN_COMPLETE;
				}
			}
			break;

		case S_WAIT_FOR_INIT:
			len = comRead(com, buffer, 1023);
			buffer[len] = 0;
			if (strstr(buffer, "OK")) {
				snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Modem Found!", com + 1);
				_port[com].status   = PORT_GOOD_MODEM;
				_port[com].selected = selected;
				_port[com].enabled  = 1;
				selected  = 0;
			} else {
				if (strstr(buffer, "ERROR")) {
					snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Incompatable Modem", com + 1);
					_port[com].status   = PORT_BAD_MODEM;
					_port[com].selected = 0;
					_port[com].enabled  = 0;
				} else {
					snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - No Modem", com + 1);
					_port[com].status   = PORT_NO_MODEM;
					_port[com].selected = 0;
					_port[com].enabled  = 0;
				}
			}
			comClose(com);
			com++;
			if (com < 4) {
				_state = S_OPEN_COM;
			} else {
				_state = S_SCAN_COMPLETE;
			}
			timerQuarterSecondsSet((TimerT *)widget, 0);
			break;

		case S_SCAN_COMPLETE:
			timerStop(_timProgress);
			settingsComShow();
			break;
	}
}
