/*
 * 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 "textbox.h"
#include "button.h"
#include "label.h"
#include "msgbox.h"
#include "timer.h"

#include "network.h"
#include "runtime.h"
#include "taglist.h"

#include "signup.h"
#include "login.h"


typedef enum SignUpStateE {
	S_START_SIGNUP = 0,
	S_SIGNUP_WAIT
} SignUpStateT;


static WindowT      *_winSignUp   = NULL;
static LabelT       *_lblRequired = NULL;
static TextboxT     *_txtEmail    = NULL;
static TextboxT     *_txtFirst    = NULL;
static TextboxT     *_txtLast     = NULL;
static TextboxT     *_txtUser     = NULL;
static TextboxT     *_txtPass1    = NULL;
static TextboxT     *_txtPass2    = NULL;
static ButtonT      *_btnCancel   = NULL;
static ButtonT      *_btnSignUp   = NULL;
static TimerT       *_timProgress = NULL;
static SignUpStateT _state        = S_START_SIGNUP;
static uint8_t      _channel      = 0;


static void    btnCancelClick(WidgetT *widget);
static void    btnMsgBoxContinue(MsgBoxButtonT button);
static void    btnMsgBoxFinish(MsgBoxButtonT button);
static void    btnSignUpClick(WidgetT *widget);
static void    packetHandler(PacketDecodeDataT *packet);
static void    setButtons(uint8_t enabled);
static void    timSignUpProgress(WidgetT *widget);
static uint8_t validateEmail(char *email);
static uint8_t validateEmailLetter(char c);
static uint8_t validateName(char *username);
static uint8_t validatePassword(char *password);
static uint8_t validateUser(char *username);


static void btnCancelClick(WidgetT *widget) {
	(void)widget;

	guiDelete(D(_winSignUp));
	netChannelRelease(_channel);
	loginShow();
}


static void btnMsgBoxContinue(MsgBoxButtonT button) {
	(void)button;

	setButtons(1);
}


static void btnMsgBoxFinish(MsgBoxButtonT button) {
	(void)button;

	guiDelete(D(_winSignUp));
	netChannelRelease(_channel);
	loginShow();
}


static void btnSignUpClick(WidgetT *widget) {
	(void)widget;

	setButtons(0);

	// Validate it.  ***TODO*** These messages could be a lot better.
	if (!validateEmail(textboxValueGet(_txtEmail))) {
		msgBoxOne("Invalid E-Mail", MSGBOX_ICON_ERROR, "Please enter a valid E-mail address.", "Okay", btnMsgBoxContinue);
		return;
	}
	if (!validateName(textboxValueGet(_txtFirst))) {
		msgBoxOne("Invalid First Name", MSGBOX_ICON_ERROR, "Please enter a valid first name.", "Okay", btnMsgBoxContinue);
		return;
	}
	if (!validateName(textboxValueGet(_txtLast))) {
		msgBoxOne("Invalid Last Name", MSGBOX_ICON_ERROR, "Please enter a valid last name.", "Okay", btnMsgBoxContinue);
		return;
	}
	if (strcmp(textboxValueGet(_txtPass1), textboxValueGet(_txtPass2)) != 0) {
		msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Passwords must match.", "Okay", btnMsgBoxContinue);
		return;
	}
	if (!validatePassword(textboxValueGet(_txtPass1))) {
		msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Please enter a valid password.", "Okay", btnMsgBoxContinue);
		return;
	}
	if (!validateUser(textboxValueGet(_txtUser))) {
		msgBoxOne("Invalid User Name", MSGBOX_ICON_ERROR, "Please enter a valid user name.", "Okay", btnMsgBoxContinue);
		return;
	}

	_state = S_START_SIGNUP;
	timerQuarterSecondsSet(_timProgress, 0);
	timerStart(_timProgress);
}


static void packetHandler(PacketDecodeDataT *packet) {
	uint16_t                 length;
	char                    *packetData;

	// Reset timeout.
	timerReset(_timProgress);

	switch (packet->packetType) {
		case PACKET_TYPE_SIGNUP_RESULT:
			packetContentUnpack(packet->data, "is", &length, &packetData);
			if (length) {
				msgBoxOne("Success!", MSGBOX_ICON_INFORMATION, packetData, "Okay", btnMsgBoxFinish);
			} else {
				msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, packetData, "Okay", btnMsgBoxContinue);
			}
			DEL(packetData);
			timerStop(_timProgress);
			break;

		default:
			logWrite("Unexpected packet received:  %d\n", packet->packetType);
			break;
	}

	packetDecodeDataDestroy(&packet);
}


static void setButtons(uint8_t enabled) {
	widgetEnableSet(W(_btnCancel), enabled);
	widgetEnableSet(W(_btnSignUp), enabled);
}


void signupShow(void) {

	TagItemT uiSignUp[] = {
		T_START,
			T_WINDOW, O(_winSignUp),
				T_TITLE, P("Create New Account"),
				T_WIDTH, 400, T_HEIGHT, 340,

				T_LABEL, O(_lblRequired),
					T_TITLE, P("All fields are required!"),
					T_X, 100, T_Y, 25,
					T_COLOR_FOREGROUND, vbeColorMake(255, 255, 0),
				T_LABEL, T_DONE,

				T_TEXTBOX, O(_txtUser),
							//  123456789012345678
					T_TITLE, P("        User Name:"),
					T_X, 40, T_Y, 64,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxUser"),
				T_TEXTBOX, T_DONE,

				T_TEXTBOX, O(_txtPass1),
							//  123456789012345678
					T_TITLE, P("         Password:"),
					T_X, 40, T_Y, 94,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxPass"),
					T_MASK, '*',
				T_TEXTBOX, T_DONE,

				T_TEXTBOX, O(_txtPass2),
							//  123456789012345678
					T_TITLE, P(" Password (again):"),
					T_X, 40, T_Y, 124,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxPass"),
					T_MASK, '*',
				T_TEXTBOX, T_DONE,

				T_TEXTBOX, O(_txtFirst),
							//  123456789012345678
					T_TITLE, P("  REAL First Name:"),
					T_X, 40, T_Y, 154,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxName"),
				T_TEXTBOX, T_DONE,

				T_TEXTBOX, O(_txtLast),
							//  123456789012345678
					T_TITLE, P("   REAL Last Name:"),
					T_X, 40, T_Y, 184,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxName"),
				T_TEXTBOX, T_DONE,

				T_TEXTBOX, O(_txtEmail),
							//  123456789012345678
					T_TITLE, P("     VALID E-Mail:"),
					T_X, 40, T_Y, 214,
					T_WIDTH, 300,
					T_LENGTH, shget(__runtimeData.integers, "maxEmail"),
				T_TEXTBOX, T_DONE,

				T_BUTTON, O(_btnCancel),
					T_TITLE, P("Cancel"),
					T_X, 25, T_Y, 270,
					T_CLICK, P(btnCancelClick),
				T_BUTTON, T_DONE,
				T_BUTTON, O(_btnSignUp),
					T_TITLE, P("Sign Up"),
					T_X, 291, T_Y, 270,
					T_CLICK, P(btnSignUpClick),
				T_BUTTON, T_DONE,
				T_TIMER, O(_timProgress),
					T_EVENT, P(timSignUpProgress),
					T_VALUE, 0,
					T_ENABLED, 0,
				T_TIMER, T_DONE,

			T_WINDOW, T_DONE,
		T_END
	};

	tagListRun(uiSignUp);
	_channel = netChannelGet(packetHandler);

	textboxValueSet(_txtEmail, "scott.duensing@gmail.com");
	textboxValueSet(_txtFirst, "Test");
	textboxValueSet(_txtLast,  "User");
	textboxValueSet(_txtUser,  "test");
	textboxValueSet(_txtPass1, "test");
	textboxValueSet(_txtPass2, "test");
}


static void timSignUpProgress(WidgetT *widget) {
	PacketEncodeDataT  encoded;
	uint16_t           length;
	char              *packetData;
	TimerT            *t           = (TimerT *)widget;

	switch (_state) {
		case S_START_SIGNUP:
			packetData = packetContentPack(&length, "sssss",
							textboxValueGet(_txtEmail),
							textboxValueGet(_txtFirst),
							textboxValueGet(_txtLast),
							textboxValueGet(_txtPass1),
							textboxValueGet(_txtUser)
						);
			// Send signup request.
			encoded.packetType = PACKET_TYPE_SIGNUP;
			encoded.control    = PACKET_CONTROL_DAT;
			encoded.channel    = _channel;
			encoded.encrypt    = 1;
			packetEncode(__packetThreadData, &encoded, packetData, length);
			packetSend(__packetThreadData, &encoded);
			DEL(packetData);
			// Wait for response.
			timerQuarterSecondsSet(_timProgress, 10 * 4);
			_state = S_SIGNUP_WAIT;
			break;

		case S_SIGNUP_WAIT:
			timerStop(t);
			msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "No response received.", "Okay", btnMsgBoxContinue);
			break;
	}
}


static uint8_t validateEmail(char *email) {

	uint16_t x;
	uint16_t step = 0;

	// This does a really terrible job of checking if a string is an email address.
	// It only has be sorta close.  The server will make sure.

	for (x=0; x<strlen(email); x++) {

		switch (step) {

			// Look for username
			case 0:
				if (validateEmailLetter(email[x])) step++;
				break;

			// Look for @
			case 1:
				if (email[x] == '@') step++;
				break;

			// Look for domain
			case 2:
				if (validateEmailLetter(email[x])) step++;
				break;

			// Look for dot
			case 3:
				if (email[x] == '.') step++;
				break;

			// Look for TLD
			case 4:
				if (validateEmailLetter(email[x])) return 1;
				break;
		}
	}

	return 0;
}


static uint8_t validateEmailLetter(char c) {
	if ((c >= '0') && (c <= '9')) return 1;
	if ((c >= 'a') && (c <= 'z')) return 1;
	if ((c >= 'A') && (c <= 'Z')) return 1;
	return 0;
}


static uint8_t validateName(char *username) {
	uint16_t  x;
	char     *allowed;
	char      needle[2];

	if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minName")) return 0;
	if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxName")) return 0;

	allowed   = shget(__runtimeData.strings, "nameAllowed");
	needle[1] = 0;
	for (x=0; x<strlen(username); x++) {
		needle[0] = username[x];
		if (!strstr(allowed, needle)) return 0;
	}

	return 1;
}


static uint8_t validatePassword(char *password) {
	uint16_t  x;
	uint16_t  lowerCount   = 0;
	uint16_t  upperCount   = 0;
	uint16_t  specialCount = 0;
	uint16_t  numericCount = 0;
	char     *passLower;
	char     *passUpper;
	char     *passSpecial;
	char     *passNumeric;
	char      needle[2];

	if ((int32_t)strlen(password) < shget(__runtimeData.integers, "minPass")) return 0;
	if ((int32_t)strlen(password) > shget(__runtimeData.integers, "maxPass")) return 0;

	passLower   = shget(__runtimeData.strings, "passLower");
	passUpper   = shget(__runtimeData.strings, "passUpper");
	passSpecial = shget(__runtimeData.strings, "passSpecial");
	passNumeric = shget(__runtimeData.strings, "passNumeric");
	needle[1] = 0;
	for (x=0; x<strlen(password); x++) {
		needle[0] = password[x];
		if (strstr(passLower,   needle)) lowerCount++;
		if (strstr(passUpper,   needle)) upperCount++;
		if (strstr(passSpecial, needle)) specialCount++;
		if (strstr(passNumeric, needle)) numericCount++;
	}

	if (lowerCount   < shget(__runtimeData.integers, "minPassLower"))   return 0;
	if (upperCount   < shget(__runtimeData.integers, "minPassUpper"))   return 0;
	if (specialCount < shget(__runtimeData.integers, "minPassSpecial")) return 0;
	if (numericCount < shget(__runtimeData.integers, "minPassNumeric")) return 0;

	return 1;
}


static uint8_t validateUser(char *username) {
	uint16_t  x;
	char     *allowed;
	char      needle[2];

	if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minUser")) return 0;
	if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxUser")) return 0;

	allowed   = shget(__runtimeData.strings, "userAllowed");
	needle[1] = 0;
	for (x=0; x<strlen(username); x++) {
		needle[0] = username[x];
		if (!strstr(allowed, needle)) return 0;
	}

	return 1;
}
