// 27 april 2015
#include "ui.h"
#include "uipriv.h"

struct bin {
	uiBin b;
	void (*baseDestroy)(uiControl *);
	uiControl *mainControl;
	intmax_t marginLeft;
	intmax_t marginTop;
	intmax_t marginRight;
	intmax_t marginBottom;
};

static void binDestroy(uiControl *c)
{
	struct bin *b = (struct bin *) c;

	// ensure clean removal by making sure the bin has no OS parent
	if (uiBinHasOSParent(uiBin(b)))
		complain("attempt to destroy bin %p while it has an OS parent", b);
	// don't chain up to base here; we need to destroy children ourselves first
	if (b->mainControl != NULL) {
		uiControlSetParent(b->mainControl, NULL);
		uiControlDestroy(b->mainControl);
	}
	// NOW we can chain up to base
	(*(b->baseDestroy))(uiControl(b));
	uiFree(b);
}

static void binPreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
	struct bin *b = (struct bin *) c;
	intmax_t left, top, right, bottom;
	intmax_t cwid, cht;

	if (b->mainControl == NULL) {
		*width = 0;
		*height = 0;
		return;
	}

	// first do margins
	left = b->marginLeft;
	top = b->marginTop;
	right = b->marginRight;
	bottom = b->marginBottom;
	uiBinTranslateMargins(uiBin(b), &left, &top, &right, &bottom, d);
	*width = left + right;
	*height = top + bottom;

	// then do the control
	uiControlPreferredSize(b->mainControl, d, &cwid, &cht);
	*width += cwid;
	*height += cht;
}

static void binSysFunc(uiControl *c, uiControlSysFuncParams *p)
{
	struct bin *b = (struct bin *) c;

	if (b->mainControl != NULL)
		uiControlSysFunc(b->mainControl, p);
}

static void binResizeChildren(uiContainer *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
	struct bin *b = (struct bin *) c;
	intmax_t left, top, right, bottom;

	if (b->mainControl == NULL)
		return;
	left = b->marginLeft;
	top = b->marginTop;
	right = b->marginRight;
	bottom = b->marginBottom;
	uiBinTranslateMargins(uiBin(b), &left, &top, &right, &bottom, d);
	x += left;
	y += top;
	width -= left + right;
	height -= top + bottom;
	uiControlResize(b->mainControl, x, y, width, height, d);
}

static void binSetMainControl(uiBin *bb, uiControl *mainControl)
{
	struct bin *b = (struct bin *) bb;

	if (b->mainControl != NULL)
		uiControlSetParent(b->mainControl, NULL);
	b->mainControl = mainControl;
	if (b->mainControl != NULL)
		uiControlSetParent(b->mainControl, uiContainer(b));
}

static void binSetMargins(uiBin *bb, intmax_t left, intmax_t top, intmax_t right, intmax_t bottom)
{
	struct bin *b = (struct bin *) bb;

	b->marginLeft = left;
	b->marginRight = right;
	b->marginTop = top;
	b->marginBottom = bottom;
}

uiBin *newBin(void)
{
	struct bin *b;

	b = uiNew(struct bin);

	uiMakeContainer(uiContainer(b));

	b->baseDestroy = uiControl(b)->Destroy;
	uiControl(b)->Destroy = binDestroy;
	uiControl(b)->PreferredSize = binPreferredSize;
	uiControl(b)->SysFunc = binSysFunc;

	uiContainer(b)->ResizeChildren = binResizeChildren;

	uiBin(b)->SetMainControl = binSetMainControl;
	uiBin(b)->SetMargins = binSetMargins;
	// these are defined by each OS's bin.c
	uiBin(b)->HasOSParent = binHasOSParent;
	uiBin(b)->SetOSParent = binSetOSParent;
	uiBin(b)->RemoveOSParent = binRemoveOSParent;
	uiBin(b)->ResizeRootAndUpdate = binResizeRootAndUpdate;
	uiBin(b)->TranslateMargins = binTranslateMargins;

	return uiBin(b);
}