// 8 september 2015
#include "uipriv_windows.hpp"
#include "area.hpp"

// TODO handle WM_DESTROY/WM_NCDESTROY
// TODO same for other Direct2D stuff
static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	uiArea *a;
	CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
	RECT client;
	WINDOWPOS *wp = (WINDOWPOS *) lParam;
	LRESULT lResult;

	a = (uiArea *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
	if (a == NULL) {
		if (uMsg == WM_CREATE) {
			a = (uiArea *) (cs->lpCreateParams);
			// assign a->hwnd here so we can use it immediately
			a->hwnd = hwnd;
			SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) a);
		}
		// fall through to DefWindowProcW() anyway
		return DefWindowProcW(hwnd, uMsg, wParam, lParam);
	}

	// always recreate the render target if necessary
	if (!a->openGL)
    {
        if (a->rt == NULL)
            a->rt = makeHWNDRenderTarget(a->hwnd);
    }

	if (areaDoDraw(a, uMsg, wParam, lParam, &lResult) != FALSE)
		return lResult;

	if (uMsg == WM_WINDOWPOSCHANGED) {
		if ((wp->flags & SWP_NOSIZE) != 0)
			return DefWindowProcW(hwnd, uMsg, wParam, lParam);
        a->width = -1;
        a->height = -1;
		uiWindowsEnsureGetClientRect(a->hwnd, &client);
		areaDrawOnResize(a, &client);
		areaScrollOnResize(a, &client);
		{
		    double w, h;
		    loadAreaSize(a, &w, &h);
		    a->ah->Resize(a->ah, a, (int)w, (int)h);
		}
		return 0;
	}

	if (areaDoScroll(a, uMsg, wParam, lParam, &lResult) != FALSE)
		return lResult;
	if (areaDoEvents(a, uMsg, wParam, lParam, &lResult) != FALSE)
		return lResult;

	// nothing done
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// control implementation

uiWindowsControlAllDefaultsExceptDestroy(uiArea)

static void uiAreaDestroy(uiControl *c)
{
    uiArea* a = uiArea(c);
    if (a->openGL && a->glcontext) freeGLContext(a->glcontext);
    uiWindowsEnsureDestroyWindow(a->hwnd);
    uiFreeControl(c);
}

static void uiAreaMinimumSize(uiWindowsControl *c, int *width, int *height)
{
	*width = c->c.MinWidth;
	if (*width < 1) *width = 1;

	*height = c->c.MinHeight;
	if (*height < 1) *height = 1;
}

ATOM registerAreaClass(HICON hDefaultIcon, HCURSOR hDefaultCursor)
{
	WNDCLASSW wc;

	ZeroMemory(&wc, sizeof (WNDCLASSW));
	wc.lpszClassName = areaClass;
	wc.lpfnWndProc = areaWndProc;
	wc.hInstance = hInstance;
	wc.hIcon = hDefaultIcon;
	wc.hCursor = hDefaultCursor;
	wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
	// this is just to be safe; see the InvalidateRect() call in the WM_WINDOWPOSCHANGED handler for more details
	wc.style = CS_HREDRAW | CS_VREDRAW;
	return RegisterClassW(&wc);
}

void unregisterArea(void)
{
	if (UnregisterClassW(areaClass, hInstance) == 0)
		logLastError(L"error unregistering uiArea window class");
}

void uiAreaSetSize(uiArea *a, int width, int height)
{
	a->scrollWidth = width;
	a->scrollHeight = height;
	areaUpdateScroll(a);
}

void uiAreaQueueRedrawAll(uiArea *a)
{
	// don't erase the background; we do that ourselves in doPaint()
	invalidateRect(a->hwnd, NULL, FALSE);
}

void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
{
	// TODO
}

void uiAreaBeginUserWindowMove(uiArea *a)
{
	HWND toplevel;

	// TODO restrict execution
	ReleaseCapture();		// TODO use properly and reset internal data structures
	toplevel = parentToplevel(a->hwnd);
	if (toplevel == NULL) {
		// TODO
		return;
	}
	// see http://stackoverflow.com/questions/40249940/how-do-i-initiate-a-user-mouse-driven-move-or-resize-for-custom-window-borders-o#40250654
	SendMessageW(toplevel, WM_SYSCOMMAND,
		SC_MOVE | 2, 0);
}

void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)
{
	HWND toplevel;
	WPARAM wParam;

	// TODO restrict execution
	ReleaseCapture();		// TODO use properly and reset internal data structures
	toplevel = parentToplevel(a->hwnd);
	if (toplevel == NULL) {
		// TODO
		return;
	}
	// see http://stackoverflow.com/questions/40249940/how-do-i-initiate-a-user-mouse-driven-move-or-resize-for-custom-window-borders-o#40250654
	wParam = SC_SIZE;
	switch (edge) {
	case uiWindowResizeEdgeLeft:
		wParam |= 1;
		break;
	case uiWindowResizeEdgeTop:
		wParam |= 3;
		break;
	case uiWindowResizeEdgeRight:
		wParam |= 2;
		break;
	case uiWindowResizeEdgeBottom:
		wParam |= 6;
		break;
	case uiWindowResizeEdgeTopLeft:
		wParam |= 4;
		break;
	case uiWindowResizeEdgeTopRight:
		wParam |= 5;
		break;
	case uiWindowResizeEdgeBottomLeft:
		wParam |= 7;
		break;
	case uiWindowResizeEdgeBottomRight:
		wParam |= 8;
		break;
	}
	SendMessageW(toplevel, WM_SYSCOMMAND,
		wParam, 0);
}


void uiAreaSetBackgroundColor(uiArea *a, int r, int g, int b)
{
    a->bgR = r;
    a->bgG = g;
    a->bgB = b;
}


uiArea *uiNewArea(uiAreaHandler *ah)
{
	uiArea *a;

	uiWindowsNewControl(uiArea, a);

	a->width = -1;
	a->height = -1;

	a->ah = ah;
	a->scrolling = FALSE;
	clickCounterReset(&(a->cc));

	// a->hwnd is assigned in areaWndProc()
	uiWindowsEnsureCreateControlHWND(0,
		areaClass, L"",
		0,
		hInstance, a,
		FALSE);

    uiAreaSetBackgroundColor(a, -1, -1, -1);

    a->openGL = 0;

	return a;
}

uiGLContext *uiAreaGetGLContext(uiArea* a)
{
    if (!a->openGL) userbug("trying to get GL context from non-GL area");

    return a->glcontext;
}

uiArea *uiNewGLArea(uiAreaHandler *ah, const unsigned int* req_versions)
{
	uiArea *a;

	uiWindowsNewControl(uiArea, a);

	a->width = -1;
	a->height = -1;

	a->ah = ah;
	a->scrolling = FALSE;
	clickCounterReset(&(a->cc));

	// a->hwnd is assigned in areaWndProc()
	uiWindowsEnsureCreateControlHWND(0,
		areaClass, L"",
		0,
		hInstance, a,
		FALSE);

    uiAreaSetBackgroundColor(a, -1, -1, -1);

    a->openGL = 1;

    for (int i = 0; req_versions[i]; i++)
    {
        int major = uiGLVerMajor(req_versions[i]);
        int minor = uiGLVerMinor(req_versions[i]);
        a->glcontext = createGLContext(a, major, minor);
        if (a->glcontext) break;
    }

	return a;
}

uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)
{
	uiArea *a;

	uiWindowsNewControl(uiArea, a);

	a->width = -1;
	a->height = -1;

	a->ah = ah;
	a->scrolling = TRUE;
	a->scrollWidth = width;
	a->scrollHeight = height;
	clickCounterReset(&(a->cc));

	// a->hwnd is assigned in areaWndProc()
	uiWindowsEnsureCreateControlHWND(0,
		areaClass, L"",
		WS_HSCROLL | WS_VSCROLL,
		hInstance, a,
		FALSE);

    uiAreaSetBackgroundColor(a, -1, -1, -1);

    a->openGL = 0; // TODO, eventually???

	// set initial scrolling parameters
	areaUpdateScroll(a);

	return a;
}