#include "wmwindow.h"
#include "array.h"
#include "font.h"


uint8_t  __MAGIC_WINDOW = 0;

static WindowT      **_windowList   = NULL;


WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...) {
	WindowT *win = NULL;

	NEW(WindowT, win);

	guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW,  x, y, w, h);
	win->title  = strdup(title);
	win->flags  = flags;
	win->close.x    = win->close.y    = win->close.x2    = win->close.y2    = 0;
	win->titlebar.x = win->titlebar.y = win->titlebar.x2 = win->titlebar.y2 = 0;
	win->minimize.x = win->minimize.y = win->minimize.x2 = win->minimize.y2 = 0;
	win->maximize.x = win->maximize.y = win->maximize.x2 = win->maximize.y2 = 0;
	win->bounds.x   = win->bounds.y   = win->bounds.x2   = win->bounds.y2   = 0;

	arrput(_windowList, win);

	return win;
}


void windowDestroy(struct WidgetS *widget, ...) {
	uint16_t i;
	WindowT *window = (WindowT *)widget;

	for (i=0; i<arrlen(_windowList); i++) {
		if (window == _windowList[i]) {
			if (_windowList[i]->title) DEL(_windowList[i]->title);
			DEL(_windowList[i]);
			arrdel(_windowList, i);
			break;
		}
	}
}


void windowPaint(struct WidgetS *widget, ...) {
	WindowT  *w   = (WindowT *)widget;
	uint16_t  x1  = w->base.r.x;
	uint16_t  y1  = w->base.r.y;
	uint16_t  x2  = w->base.r.x + w->base.r.w - 1;
	uint16_t  y2  = w->base.r.y + w->base.r.h - 1;
	uint16_t  tx1;
	uint16_t  ty1;
	uint16_t  tx2;
	uint16_t  ty2;
	uint16_t  minimizeOffset       = 0;
	ColorT    titleBackgroundColor = GUI_DARKGRAY;

	// Fake Window contents.
	videoSurfaceBoxFilled(x1, y1, x2, y2, GUI_BLACK);

	// If we need a titlebar, it's 18px.
	if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {

		// Draw title bar background.
		y1 -= 18;
		videoSurfaceBoxFilled(x1, y1, x2, y1 + 17, titleBackgroundColor);

		// Close box?
		if (w->flags & WIN_CLOSE) {
			// 26px wide, 18 tall including highlight and shadow.
			w->close.x  = x1 + 1;
			w->close.y  = y1 + 1;
			w->close.x2 = w->close.x + 24;
			w->close.y2 = w->close.y + 15;
			videoSurfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, GUI_LIGHTGRAY);
			videoSurfaceLineH(w->close.x, w->close.x2, w->close.y, GUI_WHITE);
			videoSurfaceLineV(w->close.x, w->close.y, w->close.y2, GUI_WHITE);
			// Button is 8px down, 3px tall, and 4px in on both sides.
			tx1 = w->close.x + 4;
			ty1 = w->close.y + 7;
			tx2 = w->close.x2 - 4;
			ty2 = w->close.y + 9;
			videoSurfaceLineH(tx1, tx2, ty1, GUI_WHITE);
			videoSurfacePixelSet(tx1, ty1 + 1, GUI_WHITE);
			videoSurfaceLineH(tx1, tx2, ty2, GUI_BLACK);
			videoSurfaceLineV(tx2, ty1, ty2, GUI_BLACK);
			// Set titlebar area.
			w->titlebar.x = w->close.x2 + 2;
		} else {
			// No close box - set titlebar area.
			w->titlebar.x = x1;
		}
		w->titlebar.y  = y1;
		w->titlebar.x2 = x2;
		w->titlebar.y2 = y1 + 17;

		// Maximize box?
		if (w->flags & WIN_MAXIMIZE) {
			// 26px wide, 18 tall including highlight and shadow.
			w->maximize.y  = y1 + 1;
			w->maximize.x2 = x2 - 1;
			w->maximize.x  = w->maximize.x2 - 24;
			w->maximize.y2 = w->maximize.y + 15;
			videoSurfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, GUI_LIGHTGRAY);
			videoSurfaceLineH(w->maximize.x, w->maximize.x2, w->maximize.y, GUI_WHITE);
			videoSurfaceLineV(w->maximize.x, w->maximize.y, w->maximize.y2, GUI_WHITE);
			// Button is 3px down, and 4px in on both sides.
			tx1 = w->maximize.x + 4;
			ty1 = w->maximize.y + 4;
			tx2 = w->maximize.x2 - 3;
			ty2 = w->maximize.y + 12;
			videoSurfaceLineH(tx1, tx2, ty1, GUI_WHITE);
			videoSurfaceLineV(tx1, ty1, ty2, GUI_WHITE);
			videoSurfaceLineH(tx1, tx2, ty2, GUI_BLACK);
			videoSurfaceLineV(tx2, ty1, ty2, GUI_BLACK);
			// Move minimize button over.
			minimizeOffset = 26;
			// Set titlebar area.
			w->titlebar.x2 -= 26;
		}

		// Minimize box?
		if (w->flags & WIN_MINIMIZE) {
			// 26px wide, 18 tall including highlight and shadow.
			w->minimize.y  = y1 + 1;
			w->minimize.x2 = x2 - 1 - minimizeOffset;
			w->minimize.x  = w->minimize.x2 - 24;
			w->minimize.y2 = w->minimize.y + 15;
			videoSurfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, GUI_LIGHTGRAY);
			videoSurfaceLineH(w->minimize.x, w->minimize.x2, w->minimize.y, GUI_WHITE);
			videoSurfaceLineV(w->minimize.x, w->minimize.y, w->minimize.y2, GUI_WHITE);
			tx1 = w->minimize.x + 10;
			ty1 = w->minimize.y + 6;
			tx2 = w->minimize.x2 - 8;
			ty2 = w->minimize.y + 9;
			videoSurfaceLineH(tx1, tx2, ty1, GUI_WHITE);
			videoSurfaceLineV(tx1, ty1, ty2, GUI_WHITE);
			videoSurfaceLineH(tx1, tx2, ty2, GUI_BLACK);
			videoSurfaceLineV(tx2, ty1, ty2, GUI_BLACK);
			// Set titlebar area.
			w->titlebar.x2 -= 26;
		}

		// Title font area is 12px high.
		videoSurfaceLineH(w->titlebar.x, w->titlebar.x2 - 1, w->titlebar.y, GUI_WHITE);
		videoSurfaceLineV(w->titlebar.x, w->titlebar.y, w->titlebar.y2 - 1, GUI_WHITE);
		if (w->title) {
			ty1 = w->titlebar.y + 2;
			tx1 = w->titlebar.x + 2 + (w->titlebar.x2 - w->titlebar.x - 4) * 0.5 - ((strlen(w->title) * 8) * 0.5);
			fontRender(__guiFontVGA8x14, w->title, GUI_WHITE, titleBackgroundColor, tx1, ty1);
		}
	}

	// Innermost shadow frame.  1px wide.
	x1--;
	y1--;
	x2++;
	y2++;
	videoSurfaceLineH(x1, x2, y2, GUI_WHITE);
	videoSurfaceLineV(x2, y1, y2, GUI_WHITE);
	videoSurfaceLineH(x1, x2, y1, GUI_DARKGRAY);
	videoSurfaceLineV(x1, y1, y2, GUI_DARKGRAY);

	// Frame Border.  4px wide.
	x1 -= 4;
	y1 -= 4;
	x2 += 4;
	y2 += 4;
	videoSurfaceBoxFilled(x1, y1, x1 + 3, y2, GUI_LIGHTGRAY);
	videoSurfaceBoxFilled(x2, y1, x2 - 3, y2, GUI_LIGHTGRAY);
	videoSurfaceBoxFilled(x1, y1, x2, y1 + 3, GUI_LIGHTGRAY);
	videoSurfaceBoxFilled(x1, y2, x2, y2 - 3, GUI_LIGHTGRAY);

	// Resize handle.
	if (w->flags & WIN_RESIZE) {
		ty1 = y2 - 15 - 3;
		tx1 = x2 - 15 - 3;
		videoSurfaceLineH(x2, x2 - 3, ty1, GUI_DARKGRAY);
		videoSurfaceLineH(x2, x2 - 3, ty1 + 1, GUI_WHITE);
		videoSurfaceLineV(tx1, y2, y2 - 3, GUI_DARKGRAY);
		videoSurfaceLineV(tx1 + 1, y2, y2 - 3, GUI_WHITE);
	}

	// Outermost shadow frame.  1px wide.
	x1--;
	y1--;
	x2++;
	y2++;
	videoSurfaceLineH(x1, x2, y1, GUI_WHITE);
	videoSurfaceLineV(x1, y1, y2, GUI_WHITE);
	videoSurfaceLineH(x1, x2, y2, GUI_DARKGRAY);
	videoSurfaceLineV(x2, y1, y2, GUI_DARKGRAY);
	w->bounds.x  = x1;
	w->bounds.x2 = x2;
	w->bounds.y  = y1;
	w->bounds.y2 = y2;
}


RegisterT *windowRegister(uint8_t magic) {
	static RegisterT reg = {
		"Window",
		windowPaint,
		windowDestroy,
		NULL
	};

	// One-time widget startup code.
	__MAGIC_WINDOW = magic;

	return &reg;
}


void wmPaint(EventT *event) {
	int16_t              i;
	WidgetT             *widget;
	WindowT             *win;
	static uint8_t       dragging   = 0;
	static PointT        dragOffset = { 0 };

	// Do we have windows?
	if (arrlen(_windowList) > 0) {

		// Paint all windows.
		for (i=0; i<arrlen(_windowList); i++) {
			videoSurfaceSet(__guiBackBuffer);
			widget = (WidgetT *)_windowList[i];
			widget->reg->paint(widget);
		}

		// Get top window.
		win = _windowList[arrlen(_windowList) - 1];

		// Wrap left button processing with a 'for' so we can 'break' out of it.
		for (;;) {
			// Is the left mouse button down?
			if (event->buttons & BUTTON_LEFT) {

				// DEBUG - draw active regions.  ***TODO*** No resize grabber here.
				videoSurfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_YELLOW);
				videoSurfaceBox(win->base.r.x, win->base.r.y, win->base.r.x + win->base.r.w - 1, win->base.r.y + win->base.r.h - 1, GUI_YELLOW);
				videoSurfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_RED);
				videoSurfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_RED);
				videoSurfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_RED);
				videoSurfaceBox(win->maximize.x, win->maximize.y, win->maximize.x2, win->maximize.y2, GUI_RED);

				// Are we currently dragging?
				if (dragging) {

					// Move window to new mouse location.
					win->base.r.x = event->x - dragOffset.x;
					win->base.r.y = event->y - dragOffset.y;
					break;

				} else { // Dragging.

					// Did the button just go down?
					if (event->flags & EVENT_FLAG_LEFT_DOWN) {

						// Are we on the topmost window?
						if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {

							//***TODO*** Are we inside the window content?  Most likely, check first.
							if (event->x <= (win->base.r.x + win->base.r.w - 1) && event->x >= win->base.r.x &&
								event->y <= (win->base.r.y + win->base.r.h - 1) && event->y >= win->base.r.y) {
								//***TODO*** Send to window for processing.
							}

							//***TODO*** Are we inside the close button?

							// Are we inside the title bar to begin dragging?
							if (event->x <= win->titlebar.x2 && event->x >= win->titlebar.x && event->y <= win->titlebar.y2 && event->y >= win->titlebar.y) {
								dragging = 1;
								dragOffset.x = event->x - win->base.r.x;
								dragOffset.y = event->y - win->base.r.y;
								break;
							}

							//***TODO*** Are we inside the minimize button?
							//***TODO*** Are we inside the maximize button?

						} else { // On topmost window.

							// Not over topmost window.  Search backwards to find first window we're inside.
							i = arrlen(_windowList) - 2;
							if (i >= 0) {
								for (; i>=0; i--) {
									win = _windowList[i];
									if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
										// Bring this window forward.
										arrdel(_windowList, i);
										arrput(_windowList, win);
										// If we happened to be in the title bar, go ahead and start dragging.
										if (event->x <= win->titlebar.x2 && event->x >= win->titlebar.x && event->y <= win->titlebar.y2 && event->y >= win->titlebar.y) {
											dragging = 1;
											dragOffset.x = event->x - win->base.r.x;
											dragOffset.y = event->y - win->base.r.y;
										}
										break;
									}
								}
							}

						} // On topmost window.

					} // Button just went down.

				} // Dragging.

			} else {  // Left mouse button.

				// Left mouse not down.

				// Can no longer be dragging.
				dragging = 0;
			}

			break;
		} // Left button processing.

	} // Do we have windows?

}


void wmShutdown(void) {
	while (arrlen(_windowList) > 0) {
		windowDestroy((WidgetT *)_windowList[0]);
	}
	arrfree(_windowList);
}


void wmStartup(void) {
}
