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


static void windowAllDeactivate(WidgetT *widget);
static void windowDel(WidgetT **widget);
static void windowMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void windowPaint(WidgetT *widget, uint8_t enabled, RectT pos);


void windowActiveSet(WindowT *window) {
	size_t   len;
	int16_t  i;
	WidgetT *parent = W(window)->parent;
	WidgetT *child;

	// Deactivate all windows.
	windowAllDeactivate(guiRootGet());

	// Activate us.
	GUI_SET_FLAG(W(window), WIDGET_FLAG_ACTIVE);

	// Find our window in the widget list.
	len = arrlenu(parent->children);
	for (i=len-1; i>=0; i--) {
		child = parent->children[i];
		if (child == W(window)) {
			// Move to the top of the widget list.
			arrdel(parent->children, i);
			arrput(parent->children, child);
			break;
		}
	}

	// Tell the GUI.
	_guiActiveWindow = window;
}


static void windowAllDeactivate(WidgetT *widget) {
	size_t len = arrlenu(widget->children);
	size_t x;

	// Is this a Window?
	if (widget->magic == MAGIC_WINDOW) {
		// Deactivate it.
		GUI_CLEAR_FLAG(widget, WIDGET_FLAG_ACTIVE);
		guiWidgetAndChildrenDirtySet(widget);
	}

	// Process any children.
	if (len > 0) {
		for (x=0; x<len; x++) {
			windowAllDeactivate(widget->children[x]);
		}
	}
}


static void windowDel(WidgetT **widget) {
	WindowT *w = (WindowT *)*widget;

	if (w->title) free(w->title);
}


WidgetT *windowInit(WidgetT *window, char *title) {
	WindowT *win = (WindowT *)window;

	win->base.delMethod        = windowDel;
	win->base.paintMethod      = windowPaint;
	win->base.mouseEventMethod = windowMouseEvent;
	win->flags                 = 0;
	win->title                 = NULL;

	windowTitleSet(win, title);

	GUI_SET_FLAG(window, WIDGET_FLAG_OWNS_SURFACE);
	win->base.surface = surfaceCreate(win->base.pos.w, win->base.pos.h);
	if (!win->base.surface) {
		free(win->title);
		free(win);
		return NULL;
	}

	return window;
}


static void windowMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {

	WindowT *window = (WindowT *)widget;

	(void)x;

	// Raise window?
	if (event == MOUSE_EVENT_LEFT_UP && !GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) windowActiveSet(window);

	// Start dragging?
	if (event == MOUSE_EVENT_LEFT_DOWN && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
		// Are we on the draggable area of the titlebar / borders?
		if (y < _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]) {
			// Start dragging.
			_guiDragWidget  = widget;
			_guiDragOffsetX = mouse->x - window->base.pos.x;
			_guiDragOffsetY = mouse->y - window->base.pos.y;
		}
	}

	// Still dragging?  We use raw mouse data here because it's possible to drag too quickly and trigger "OUT" events.
	if (_guiDragWidget == widget && mouse->buttonLeft && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
		// Move window.
		window->base.pos.x = mouse->x - _guiDragOffsetX;
		window->base.pos.y = mouse->y - _guiDragOffsetY;
		// Keep it on the screen.
		if (window->base.pos.x < 0) window->base.pos.x = 0;
		if (window->base.pos.x + window->base.pos.w > vbeDisplayWidthGet()) window->base.pos.x = vbeDisplayWidthGet() - window->base.pos.w;
		if (window->base.pos.y < 0) window->base.pos.y = 0;
		if (window->base.pos.y + window->base.pos.h > vbeDisplayHeightGet()) window->base.pos.y = vbeDisplayHeightGet() - window->base.pos.h;
	}

	// Be sure we stop dragging on mouse up or move out.
	if (_guiDragWidget == widget && event == MOUSE_EVENT_LEFT_UP) {
		_guiDragWidget = NULL;
	}
}


WindowT *windowNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) {
	WindowT *window = (WindowT *)malloc(sizeof(WindowT));
	WidgetT *widget  = NULL;

	if (!window) return NULL;

	widget = widgetInit(W(window), MAGIC_WINDOW, x, y, w, h,
		_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3,
		_guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT],
		_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4,
		_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4);
	if (!widget) {
		free(window);
		return NULL;
	}

	window = (WindowT *)windowInit((WidgetT *)window, title);

	return window;
}


static void windowPaint(WidgetT *widget, uint8_t enabled, RectT pos) {
	WindowT  *w          = (WindowT *)widget;
	uint16_t  x2;
	uint16_t  y2;
	ColorT    background;
	ColorT    text;

	x2         = pos.w - 1;
	y2         = pos.h - 1;
	background = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_INACTIVE];
	text       = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE];

	// Background.
	surfaceClear(_guiColor[COLOR_WINDOW_BACKGROUND]);

	// Outer edge.
	surfaceHighlightFrameDraw(0, 0, x2, y2, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]);

	// Inner edge - skip METRIC_WINDOW_BORDER_WIDTH pixels.  Be sure shadow and highlight are not included in the width.
	surfaceHighlightFrameDraw(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 2, y2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 2, _guiColor[COLOR_WINDOW_SHADOW], _guiColor[COLOR_WINDOW_HIGHLIGHT]);

	// Title bar - METRIC_WINDOW_TITLE_HEIGHT pixels high.  Be sure shadow and highlight are not included in the width.
	surfaceHighlightFrameDraw(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]);
	surfaceRectangleFilledDraw(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 2, background);

	fontRender(_guiFont, w->title, text, background, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 16, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 5);
}


void windowTitleSet(WindowT *window, char *title) {
	if (window->title) free(window->title);
	window->title = strdup(title);
}
