// 30 may 2015
#include "uipriv_windows.h"

// This is a special internal control type that handles tab pages.
// This doesn't use the container class, but rather a subclassed WC_DIALOG, as that supports tab textures properly.
// The standard property sheet control does the same thing.

struct tabPage {
	uiControl c;
	HWND hwnd;
	uiControl *child;
	int margined;
	void (*baseResize)(uiControl *, intmax_t, intmax_t, intmax_t, intmax_t, uiSizing *);
};

uiDefineControlType(tabPage, tabPageType, struct tabPage)

// don't override CommitDestroy(); we destroy the child with a separate function

static uintptr_t tabPageHandle(uiControl *c)
{
	struct tabPage *t = (struct tabPage *) c;

	return (uintptr_t) (t->hwnd);
}

// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
#define tabMargin 7

static void tabPagePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
	struct tabPage *t = (struct tabPage *) c;

	*width = 0;
	*height = 0;
	if (t->child != NULL)
		uiControlPreferredSize(t->child, d, width, height);
	if (t->margined) {
		*width += 2 * uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX);
		*height += 2 * uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY);
	}
}

static void tabPageResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
	struct tabPage *t = (struct tabPage *) c;
	RECT r;
	uiSizing *dchild;

	(*(t->baseResize))(uiControl(t), x, y, width, height, d);

	if (t->child == NULL)
		return;

	dchild = uiControlSizing(uiControl(t));

	if (GetClientRect(t->hwnd, &r) == 0)
		logLastError("error getting tab page client rect in tabPageResize()");
	if (t->margined) {
		r.left += uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX);
		r.top += uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY);
		r.right -= uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX);
		r.bottom -= uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY);
	}
	// this rect is in client coordinates; we need toplevel window coordinates
	mapWindowRect(t->hwnd, dchild->Sys->CoordFrom, &r);

	uiControlResize(t->child, r.left, r.top, r.right - r.left, r.bottom - r.top, dchild);

	uiFreeSizing(dchild);
}

static void tabPageContainerUpdateState(uiControl *c)
{
	struct tabPage *t = (struct tabPage *) c;

	if (t->child != NULL)
		uiControlUpdateState(t->child);
}

// dummy dialog procedure; see below for details
// let's handle parent messages here to avoid needing to subclass
static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult;

	if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) {
		SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONG_PTR) lResult);
		return TRUE;
	}
	// unthemed dialogs don't respond to WM_PRINTCLIENT
	// fortunately they don't have any special painting
	if (uMsg == WM_PRINTCLIENT) {
		// don't worry about the return value; hopefully DefWindowProcW() caught it (if not the dialog procedure itself)
		// we COULD paint the dialog background brush ourselves but meh, it works
		SendMessageW(hwnd, WM_ERASEBKGND, wParam, lParam);
		// and pretend we did nothing just so the themed dialog can still paint its content
		return FALSE;
	}
	if (uMsg == WM_INITDIALOG)
		return TRUE;
	return FALSE;
}

uiControl *newTabPage(void)
{
	struct tabPage *t;
	HRESULT hr;

	t = (struct tabPage *) uiWindowsNewSingleHWNDControl(tabPageType());

	// unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice
	t->hwnd = CreateDialogW(hInstance, MAKEINTRESOURCE(rcTabPageDialog),
		utilWindow, dlgproc);
	if (t->hwnd == NULL)
		logLastError("error creating tab page in newTabPage()");

	hr = EnableThemeDialogTexture(t->hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE | ETDT_ENABLETAB);
	if (hr != S_OK)
		logHRESULT("error setting tab page background in newTabPage()", hr);

	uiControl(t)->Handle = tabPageHandle;
	uiControl(t)->PreferredSize = tabPagePreferredSize;
	t->baseResize = uiControl(t)->Resize;
	uiControl(t)->Resize = tabPageResize;
	uiControl(t)->ContainerUpdateState = tabPageContainerUpdateState;

	return uiControl(t);
}

int tabPageMargined(uiControl *c)
{
	struct tabPage *t = (struct tabPage *) c;

	return t->margined;
}

void tabPageSetMargined(uiControl *c, int margined)
{
	struct tabPage *t = (struct tabPage *) c;

	t->margined = margined;
}

void tabPageDestroyChild(uiControl *c)
{
	struct tabPage *t = (struct tabPage *) c;

	uiControlSetParent(t->child, NULL);
	uiControlDestroy(t->child);
	t->child = NULL;
}

void tabPagePreserveChild(uiControl *c)
{
	struct tabPage *t = (struct tabPage *) c;

	uiControlSetParent(t->child, NULL);
	t->child = NULL;
}

void tabPageSetChild(uiControl *c, uiControl *child)
{
	struct tabPage *t = (struct tabPage *) c;

	t->child = child;
	t->child = child;
	uiControlSetParent(t->child, uiControl(t));
}