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

#include "gui.h"
#include "widget.h"

#include "button.h"
#include "checkbox.h"
#include "frame.h"
#include "label.h"
#include "listbox.h"
#include "picture.h"
#include "radio.h"
#include "terminal.h"
#include "textbox.h"
#include "timer.h"
#include "updown.h"
#include "window.h"


typedef struct WidgetItemS {
	TagItemT   type;
	WidgetT  **widget;
	WidgetT  **parent;
	TagItemT  *tagList;
} WidgetItemT;


static WidgetItemT **_widgetList = NULL;


static void     tagListWidgetAttributeHandle(void);
static uint32_t tagListWidgetHandle(TagItemT *list, uint32_t index, WidgetT **parent);


uint8_t tagListRun(TagItemT *list) {

	uint32_t index = 1;

	// Is this a list?  Lame check.
	if (list[0] != T_START) return 1;

	// Process until we hit the end of the list.
	while (list[index] != T_END) {

		// Is this a widget?
		if (list[index] > T_META_START_OF_WIDGETS && list[index] < T_META_END_OF_WIDGETS) {
			index = tagListWidgetHandle(list, index, NULL);
			tagListWidgetAttributeHandle();
		}

	}  // while

	return 0;
}


static void tagListWidgetAttributeHandle(void) {
	WidgetItemT    *w;
	TagItemT        t;
	TagItemT        v;
	uint32_t        i;
	uint32_t        j;
	WidgetT        *widget;
	WidgetT        *parent;
	char           *title;
	char           *filename;
	RectT           pos;
	uint8_t         hasWidth;
	uint8_t         hasHeight;
	widgetCallback  click;
	void           *userdata;
	int32_t         valueInt;
	char           *valueString;
	uint8_t         hasValue;
	ColorT          colorActive;
	ColorT          colorForeground;
	ColorT          colorBackground;
	uint8_t         hasColorActive;
	uint8_t         hasColorForeground;
	uint8_t         hasColorBackground;
	int8_t          selected;
	uint32_t        group;
	int32_t         length;
	int32_t         maximum;
	int32_t         minimum;
	int32_t         step;
	char          **items;
	uint32_t        indexAttribute;
	uint8_t         hasIndex;
	uint8_t         enabled;
	uint8_t         visible;
	char            mask;

	// Process generated lists in reverse.
	while (arrlen(_widgetList) > 0) {
		w = arrpop(_widgetList);

		// Reset all attributes for this widget.
		title              =  NULL;
		filename           =  NULL;
		pos.x              =  0;
		pos.y              =  0;
		pos.w              =  0;
		pos.h              =  0;
		hasWidth           =  0;
		hasHeight          =  0;
		click              =  NULL;
		userdata           =  NULL;
		hasValue           =  0;
		valueInt           =  0;
		valueString        =  NULL;
		colorActive        =  0;
		colorBackground    =  0;
		colorForeground    =  0;
		hasColorActive     =  0;
		hasColorBackground =  0;
		hasColorForeground =  0;
		selected           = -1;
		group              =  0;
		length             = -1;
		minimum            =  0;
		maximum            =  255;
		step               =  10;
		items              =  NULL;
		indexAttribute     =  0;
		hasIndex           =  0;
		enabled            =  1;
		visible            =  1;
		mask               =  0;

		// Parse provided attributes.
		for (i=0; i<arrlen(w->tagList); i+=2) {
			t = w->tagList[i];
			v = w->tagList[i + 1];

			switch (t) {

				case T_COLOR_ACTIVE:
					colorActive    = (ColorT)v;
					hasColorActive = 1;
					break;

				case T_COLOR_BACKGROUND:
					colorBackground    = (ColorT)v;
					hasColorBackground = 1;
					break;

				case T_COLOR_FOREGROUND:
					colorForeground    = (ColorT)v;
					hasColorForeground = 1;
					break;

				case T_CLICK:
				case T_EVENT:
					click = (widgetCallback)v;
					break;

				case T_ENABLED:
					enabled = (v != 0 ? 1 : 0);
					break;

				case T_FILENAME:
					filename = (char *)v;
					break;

				case T_GROUP:
					group = v;
					break;

				case T_HEIGHT:
					pos.h     = v;
					hasHeight = 1;
					break;

				case T_INDEX:
					indexAttribute = v;
					hasIndex       = 1;
					break;

				case T_ITEM:
					arrput(items, (char *)v);
					break;

				case T_LENGTH:
					length = v;
					break;

				case T_MASK:
					mask = v;
					break;

				case T_MAXIMUM:
					maximum = v;
					break;

				case T_MINIMUM:
					minimum = v;
					break;

				case T_SELECTED:
					selected = (v != 0 ? 1 : 0);
					break;

				case T_STEP:
					step = v;
					break;

				case T_TITLE:
					title = (char *)v;
					break;

				case T_USER_DATA:
					userdata = (void *)v;
					break;

				case T_VALUE:
					valueInt    = v;
					valueString = (void *)v;
					hasValue    = 1;
					break;

				case T_VISIBLE:
					visible = (v != 0 ? 1 : 0);
					break;

				case T_WIDTH:
					pos.w    = v;
					hasWidth = 1;
					break;

				case T_X:
					pos.x = v;
					break;

				case T_Y:
					pos.y = v;
					break;

			}
		}

		// Clear taglist after processing.
		while (arrlen(w->tagList) > 0) {
			arrpop(w->tagList);
		}
		arrfree(w->tagList);

		// Find our widget object and parent.
		widget = *w->widget;
		if (w->parent == NULL) {
			parent = NULL;
		} else {
			parent = *w->parent;
		}

		// Auto-center windows if position not specified.
		if (pos.x == 0 && pos.y == 0 && w->type == T_WINDOW) {
			pos.x = vbeDisplayWidthGet() / 2 - pos.w / 2;
			pos.y = vbeDisplayHeightGet() / 2 - pos.h / 2;
		}

		// Create the widget.
		switch (w->type) {

			case T_BUTTON:
				widget = W(buttonNew(pos.x, pos.y, title, click));
				break;

			case T_CHECKBOX:
				widget = W(checkboxNew(pos.x, pos.y, title));
				if (hasValue)      checkboxValueSet((CheckboxT *)widget, valueInt);
				if (selected >= 0) checkboxValueSet((CheckboxT *)widget, selected);
				break;

			case T_FRAME:
				widget = W(frameNew(pos.x, pos.y, pos.w, pos.h, title));
				break;

			case T_LABEL:
				widget = W(labelNew(pos.x, pos.y, title));
				if (hasColorActive)     labelColorActiveSet((LabelT *)widget, colorActive);
				if (hasColorBackground) labelColorBackgroundSet((LabelT *)widget, colorBackground);
				if (hasColorForeground) labelColorForegroundSet((LabelT *)widget, colorForeground);
				if (click != NULL)      labelClickHandlerSet((LabelT *)widget, click);
				break;

			case T_LISTBOX:
				widget = W(listboxNew(pos.x, pos.y, pos.w, pos.h, title));
				listboxStepSet((ListboxT *)widget, step);
				for (j=0; j<arrlen(items); j++) {
					listboxItemAdd((ListboxT *)widget, items[j]);
				}
				if (hasValue) listboxValueSet((ListboxT *)widget, valueString);
				if (hasIndex) listboxIndexSet((ListboxT *)widget, indexAttribute);
				break;

			case T_PICTURE:
				widget = W(pictureNew(pos.x, pos.y, filename));
				if (click != NULL) pictureClickHandlerSet((PictureT *)widget, click);
				break;

			case T_RADIOBUTTON:
				widget = W(radioNew(pos.x, pos.y, title, group));
				if (hasValue && valueInt != 0) radioSelectedSet((RadioT *)widget);
				if (selected == 1)             radioSelectedSet((RadioT *)widget);
				break;

			case T_TERMINAL:
				widget = W(terminalNew(pos.x, pos.y, pos.w, pos.h));
				if (hasColorBackground) terminalBackgroundSet((TerminalT *)widget, colorBackground);
				if (hasColorForeground) terminalForegroundSet((TerminalT *)widget, colorForeground);
				if (hasColorBackground || hasColorForeground) terminalScreenClear((TerminalT *)widget);
				break;

			case T_TEXTBOX:
				widget = W(textboxNew(pos.x, pos.y, pos.w, title));
				if (hasValue) textboxValueSet((TextboxT *)widget, valueString);
				if (length > 0) textboxLengthMaxSet((TextboxT *)widget, length);
				textboxPasswordCharacterSet((TextboxT *)widget, mask);
				break;

			case T_TIMER:
				if (hasValue && click) {
					widget = W(timerNew(click, (uint32_t)valueInt));
					if (!enabled) timerStop((TimerT *)widget);
				}
				break;

			case T_UPDOWN:
				widget = W(updownNew(pos.x, pos.y, minimum, maximum, step, title));
				if (hasValue) updownValueSet((UpdownT *)widget, valueInt);
				break;

			case T_WINDOW:
				widget = W(windowNew(pos.x, pos.y, pos.w, pos.h, title));
				break;
		}

		if (userdata != NULL) guiUserDataSet(widget, userdata);

		widgetEnableSet(widget, enabled);
		widgetVisibleSet(widget, visible);

		// Store everything we did.
		*w->widget = widget;

		// Attach it.
		if (parent == NULL) {
			guiAttach(guiRootGet(), widget);
		} else {
			guiAttach(parent, widget);
		}

		// If we allocated items, release them.
		if (items != NULL) {
			arrfree(items);
			items = NULL;
		}

		// Free this widget list.
		free(w);
	}

	arrfree(_widgetList);
	_widgetList = NULL;
}


static uint32_t tagListWidgetHandle(TagItemT *list, uint32_t index, WidgetT **parent) {
	TagItemT      widgetType = 0;
	WidgetItemT  *wi         = (WidgetItemT *)malloc(sizeof(WidgetItemT));

	// Lists at this point consist of:
	// WidgetType, WidgetObjectPointer, WidgetAttributeTag, WidgetAttributeValue, WidgetAttributeTag, WidgetAttributeValue, ..., WidgetType, T_END, ...

	widgetType  = list[index++];
	wi->type    = widgetType;                 // Save widget type.
	wi->widget  = (WidgetT **)list[index++];  // Save widget object pointer.
	wi->parent  = parent;                     // Save parent widget.
	wi->tagList = NULL;                       // Prepare widget tag item array.

	while (1) {

		// End of widget definition?
		if (list[index] == widgetType && list[index+1] == T_DONE) {
			// Skip over widget T_END tag pair.
			index += 2;
			// Save this widget data.
			arrput(_widgetList, wi);
			break;
		}

		// Start of nested widget?
		if (list[index] > T_META_START_OF_WIDGETS && list[index] < T_META_END_OF_WIDGETS) {
			index = tagListWidgetHandle(list, index, wi->widget);
			continue;
		}

		// Save this tag item pair for later.
		arrput(wi->tagList, list[index++]);
		arrput(wi->tagList, list[index++]);

	}  // while

	return index;
}
