/*
 * 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 "msgbox.h"

#include "font.h"

#include "window.h"
#include "button.h"
#include "label.h"
#include "picture.h"


#define WIN_MARGIN_TOP    26
#define WIN_MARGIN_LEFT    7
#define WIN_MARGIN_RIGHT   8
#define WIN_MARGIN_BOTTOM  8

#define BTN_MARGIN_TOP     4
#define BTN_MARGIN_LEFT   14
#define BTN_MARGIN_RIGHT  14
#define BTN_MARGIN_BOTTOM  4

#define MSG_PADDING 10

#define ICN_SIZE    32


typedef struct MessageBoxContextS {
	msgBoxCallback  cbOne;
	msgBoxCallback  cbTwo;
	msgBoxCallback  cbThree;
	WindowT        *winDialog;
	PictureT       *picIcon;
	LabelT         *lblText;
	ButtonT        *btnOne;
	ButtonT        *btnTwo;
	ButtonT        *btnThree;
} MessageBoxContextT;


//***TODO*** This depends on the cache system which isn't really part of the GUI.
static char *_iconFiles[MSGBOX_ICON_COUNT] = {
	"gui:exclamation.png",
	"gui:warning.png",
	"gui:information.png",
	"gui:message.png",
	"gui:question.png"
};


static void btnMsgBox(WidgetT *widget);


static void btnMsgBox(WidgetT *widget) {
	MessageBoxContextT *context = guiUserDataGet(widget);

	// Remove us from the display.
	guiDelete(D(context->winDialog));

	// Call whoever wanted called.
	if (widget == W(context->btnOne)) {
		if (context->cbOne) context->cbOne(MSGBOX_BUTTON_ONE);
	}
	if (widget == W(context->btnTwo)) {
		if (context->cbTwo) context->cbTwo(MSGBOX_BUTTON_TWO);
	}
	if (widget == W(context->btnThree)) {
		if (context->cbThree) context->cbThree(MSGBOX_BUTTON_THREE);
	}

	// Delayed Free allows freeing of data that may be in use by the GUI system at a safe time.
	guiDelayedFree((void **)&context);
}


void msgBoxOne(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne) {
	return msgBoxThree(title, icon, message, buttonOne, callbackOne, NULL, NULL, NULL, NULL);
}


void msgBoxThree(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo, char *buttonThree, msgBoxCallback callbackThree) {
	uint16_t  w         = 0;
	uint16_t  h         = 0;
	uint16_t  x         = 0;
	uint16_t  y         = 0;
	uint16_t  t         = 0;
	uint16_t  textH     = 0;
	uint16_t  textW     = 0;
	uint16_t  iconW     = 0;
	uint16_t  newW      = 0;
	uint16_t  cursorH   = 0;
	char     *token     = NULL;
	char     *text      = NULL;
	MessageBoxContextT *context = NULL;

	NEW(MessageBoxContextT, context);

	// Null these for later.
	context->btnOne   = NULL;
	context->btnTwo   = NULL;
	context->btnThree = NULL;

	// Remember callbacks for later.
	context->cbOne   = callbackOne;
	context->cbTwo   = callbackTwo;
	context->cbThree = callbackThree;

	// Calculate size of dialog box window.

	// Initial minimum window width is the left and right margins + font width * (title length + 3).
	w = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * (strlen(title) + 3));

	// Initial minimum window height is the top and bottom margins + font height + button height + (padding * 2).
	h = WIN_MARGIN_TOP + WIN_MARGIN_BOTTOM + fontHeightGet(__guiFont) + (BTN_MARGIN_TOP + BTN_MARGIN_BOTTOM + fontHeightGet(__guiFont)) + (MSG_PADDING * 2);

	// Count lines of text and add up height.  Find widest line.
	text = strdup(message);
	token = strtok(text, "\n");
	while (token) {
		textH += fontHeightGet(__guiFont);
		if (strlen(token) > textW) {
			textW = strlen(token);
		}
		token = strtok(NULL, "\n");
	}
	free(text);
	textW *= fontWidthGet(__guiFont);

	// Initial cursor move and window sizing are based on text height.
	cursorH = textH;

	// Is there an icon?
	if (icon > MSGBOX_ICON_NONE && icon < MSGBOX_ICON_COUNT) {
		// Is the icon taller than the message text?
		if (ICN_SIZE > textH) {
			// Use icon height.
			cursorH = ICN_SIZE;
		}
		iconW = ICN_SIZE + MSG_PADDING;
	}

	// Add icon and/or text height to window.
	h += cursorH;

	// Do we need to make the dialog wider to contain the text and icon?
	newW = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + iconW + textW + (MSG_PADDING * 2);  // Only 2 here because iconW includes one.
	if (newW > w) w = newW;

	// Do we need to make the dialog wider to contain the buttons?
	newW = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonOne));
	if (buttonTwo) newW += MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonTwo));
	if (buttonThree) newW += MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonThree));
	if (newW > w) w = newW;

	// Draw dialog.

	h -= 5;  // Height is off for some reason.  At least, to me, it doesn't look right.
	context->winDialog = windowNew(vbeDisplayWidthGet() / 2 - w / 2, vbeDisplayHeightGet() / 2 - h / 2, w, h, title);
	guiAttach(guiRootGet(), W(context->winDialog));

	// Initial x cursor is the (implied window left margin +) padding
	x = MSG_PADDING;

	// Initial y cursor is the (implied window top margin +) padding
	y = MSG_PADDING;

	// Load proper icon, if desired.
	if (icon > MSGBOX_ICON_NONE && icon < MSGBOX_ICON_COUNT) {
		context->picIcon = pictureNew(x, y, _iconFiles[icon - 1]);
		guiAttach(W(context->winDialog), W(context->picIcon));
	}

	// Draw message text.
	t = y;
	x += iconW;
	text = strdup(message);
	token = strtok(text, "\n");
	while (token) {
		context->lblText = labelNew(x, t, token);
		guiAttach(W(context->winDialog), W(context->lblText));
		t += fontHeightGet(__guiFont);
		token = strtok(NULL, "\n");
	}
	free(text);

	// Move to where buttons belong.
	x = w - (WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonOne)));
	y += cursorH + MSG_PADDING;

	// We always have at least one button.
	context->btnOne = buttonNew(x, y, buttonOne, btnMsgBox);
	guiUserDataSet(W(context->btnOne), context);
	guiAttach(W(context->winDialog), W(context->btnOne));

	// Two buttons?
	if (buttonTwo) {
		x -= (MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonTwo)));
		context->btnTwo = buttonNew(x, y, buttonTwo, btnMsgBox);
		guiUserDataSet(W(context->btnTwo), context);
		guiAttach(W(context->winDialog), W(context->btnTwo));
	}

	// Three buttons?
	if (buttonThree) {
		x -= (MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(__guiFont) * strlen(buttonThree)));
		context->btnThree = buttonNew(x, y, buttonThree, btnMsgBox);
		guiUserDataSet(W(context->btnThree), context);
		guiAttach(W(context->winDialog), W(context->btnThree));
	}
}


void msgBoxTwo(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo) {
	return msgBoxThree(title, icon, message, buttonOne, callbackOne, buttonTwo, callbackTwo, NULL, NULL);
}
