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

// desired behavior:
// - tab moves between /entire groups/
// - arrow keys navigate between radio buttons

struct radiobuttons {
	uiRadioButtons r;
	struct ptrArray *hwnds;
	uiControl *parent;
	uintptr_t insertAfter;			// safe to be 0 initially (either not in a container or trully the first in the z-order)
};

uiDefineControlType(uiRadioButtons, uiTypeRadioButtons, struct radiobuttons)

// TODO arrow keys don't work for changing items

// TODO this wrecks the z-order
static BOOL onWM_COMMAND(uiControl *c, HWND clicked, WORD code, LRESULT *lResult)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	WPARAM check;
	uintmax_t i;
	HWND hwnd;

	if (code != BN_CLICKED)
		return FALSE;
	for (i = 0; i < r->hwnds->len; i++) {
		hwnd = ptrArrayIndex(r->hwnds, HWND, i);
		check = BST_UNCHECKED;
		if (clicked == hwnd)
			check = BST_CHECKED;
		SendMessage(hwnd, BM_SETCHECK, check, 0);
	}
	*lResult = 0;
	return TRUE;
}

static void radiobuttonsCommitDestroy(uiControl *c)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	HWND hwnd;

	while (r->hwnds->len != 0) {
		hwnd = ptrArrayIndex(r->hwnds, HWND, 0);
		ptrArrayDelete(r->hwnds, 0);
		uiWindowsUnregisterWM_COMMANDHandler(hwnd);
		uiWindowsUtilDestroy(hwnd);
	}
	ptrArrayDestroy(r->hwnds);
}

// radio buttons have more than one handle
// if we allow deletion, the handles are not permanent
static uintptr_t radiobuttonsHandle(uiControl *c)
{
	return 0;
}

static void radiobuttonsCommitSetParent(uiControl *c, uiControl *parent)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	HWND hwnd;
	uintmax_t i;

	r->parent = parent;
	for (i = 0; i < r->hwnds->len; i++) {
		hwnd = ptrArrayIndex(r->hwnds, HWND, i);
		uiWindowsUtilSetParent(hwnd, r->parent);
	}
}

// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
#define radiobuttonHeight 10
// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
#define radiobuttonXFromLeftOfBoxToLeftOfLabel 12

static void radiobuttonsPreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	uintmax_t i;
	intmax_t wid, maxwid;

	maxwid = 0;
	for (i = 0; i < r->hwnds->len; i++) {
		wid = uiWindowsWindowTextWidth(ptrArrayIndex(r->hwnds, HWND, i));
		if (maxwid < wid)
			maxwid = wid;
	}
	*width = uiWindowsDlgUnitsToX(radiobuttonXFromLeftOfBoxToLeftOfLabel, d->Sys->BaseX) + maxwid;
	*height = uiWindowsDlgUnitsToY(radiobuttonHeight, d->Sys->BaseY) * r->hwnds->len;
}

static void radiobuttonsResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	intmax_t height1;
	intmax_t h;
	uintmax_t i;
	HWND hwnd;

	height1 = uiWindowsDlgUnitsToY(radiobuttonHeight, d->Sys->BaseY);
	for (i = 0; i < r->hwnds->len; i++) {
		hwnd = ptrArrayIndex(r->hwnds, HWND, i);
		h = height1;
		if (h > height)		// clip to height
			h = height;
		moveWindow(hwnd, x, y, width, h, d);
		y += height1;
		height -= height1;
		if (height <= 0)		// clip to height
			break;
	}
}

static uiSizing *radiobuttonsSizing(uiControl *c)
{
	complain("attempt to call uiControlSizing() on uiRadioButtons %p", c);
	return NULL;
}

#define COMMIT(n, f) \
	static void radiobuttonsCommit ## n(uiControl *c) \
	{ \
		struct radiobuttons *r = (struct radiobuttons *) c; \
		uintmax_t i; \
		HWND hwnd; \
		for (i = 0; i < r->hwnds->len; i++) { \
			hwnd = ptrArrayIndex(r->hwnds, HWND, i); \
			f(hwnd); \
		} \
	}
COMMIT(Show, uiWindowsUtilShow)
COMMIT(Hide, uiWindowsUtilHide)
COMMIT(Enable, uiWindowsUtilEnable)
COMMIT(Disable, uiWindowsUtilDisable)

static uintptr_t radiobuttonsStartZOrder(uiControl *c)
{
	struct radiobuttons *r = (struct radiobuttons *) c;

	return r->insertAfter;
}

static uintptr_t radiobuttonsSetZOrder(uiControl *c, uintptr_t insertAfter)
{
	struct radiobuttons *r = (struct radiobuttons *) c;
	uintmax_t i;
	HWND hwnd;

	r->insertAfter = insertAfter;
	for (i = 0; i < r->hwnds->len; i++) {
		hwnd = ptrArrayIndex(r->hwnds, HWND, i);
		uiWindowsUtilSetZOrder(hwnd, insertAfter);
		insertAfter = (uintptr_t) hwnd;
	}
	return insertAfter;
}

static int radiobuttonsHasTabStops(uiControl *c)
{
	struct radiobuttons *r = (struct radiobuttons *) c;

	return r->hwnds->len != 0;
}

static void radiobuttonsAppend(uiRadioButtons *rr, const char *text)
{
	struct radiobuttons *r = (struct radiobuttons *) rr;
	HWND hwnd;
	WCHAR *wtext;
	HWND after;

	wtext = toUTF16(text);
	hwnd = uiWindowsUtilCreateControlHWND(0,
		L"button", wtext,
		BS_RADIOBUTTON | WS_TABSTOP,
		hInstance, NULL,
		TRUE);
	uiFree(wtext);
	uiWindowsUtilSetParent(hwnd, r->parent);
	uiWindowsRegisterWM_COMMANDHandler(hwnd, onWM_COMMAND, uiControl(r));

	// maintain z-order
	if (r->hwnds->len == 0)		// first item
		uiWindowsUtilSetZOrder(hwnd, r->insertAfter);
	else {
		after = ptrArrayIndex(r->hwnds, HWND, r->hwnds->len - 1);
		uiWindowsUtilSetZOrder(hwnd, (uintptr_t) after);
	}

	ptrArrayAppend(r->hwnds, hwnd);
	uiControlQueueResize(uiControl(r));
}

uiRadioButtons *uiNewRadioButtons(void)
{
	struct radiobuttons *r;

	r = (struct radiobuttons *) uiNewControl(uiTypeRadioButtons());

	r->hwnds = newPtrArray();

	uiControl(r)->CommitDestroy = radiobuttonsCommitDestroy;
	uiControl(r)->Handle = radiobuttonsHandle;
	uiControl(r)->CommitSetParent = radiobuttonsCommitSetParent;
	uiControl(r)->PreferredSize = radiobuttonsPreferredSize;
	uiControl(r)->Resize = radiobuttonsResize;
	uiControl(r)->Sizing = radiobuttonsSizing;
	uiControl(r)->CommitShow = radiobuttonsCommitShow;
	uiControl(r)->CommitHide = radiobuttonsCommitHide;
	uiControl(r)->CommitEnable = radiobuttonsCommitEnable;
	uiControl(r)->CommitDisable = radiobuttonsCommitDisable;
	uiControl(r)->StartZOrder = radiobuttonsStartZOrder;
	uiControl(r)->SetZOrder = radiobuttonsSetZOrder;
	uiControl(r)->HasTabStops = radiobuttonsHasTabStops;

	uiRadioButtons(r)->Append = radiobuttonsAppend;

	return uiRadioButtons(r);
}