// 7 april 2015
#include "uipriv_unix.h"

// TODO
// - the move the label box on GTK+ is not sized halfway, or the label below it is not; one of the two

struct box {
	uiBox b;
	GtkWidget *widget;
	GtkBox *box;
	void (*baseCommitDestroy)(uiControl *);
	// TODO switch to GArray
	struct ptrArray *controls;
	int vertical;
	int padded;
};

struct boxControl {
	uiControl *c;
	int stretchy;
};

uiDefineControlType(uiBox, uiTypeBox, struct box)

static void boxCommitDestroy(uiControl *c)
{
	struct box *b = (struct box *) c;
	struct boxControl *bc;

	// don't chain up to base here; we need to destroy children ourselves first
	while (b->controls->len != 0) {
		bc = ptrArrayIndex(b->controls, struct boxControl *, 0);
		uiControlSetParent(bc->c, NULL);
		uiControlDestroy(bc->c);
		ptrArrayDelete(b->controls, 0);
		uiFree(bc);
	}
	ptrArrayDestroy(b->controls);
	// NOW we can chain up to base
	(*(b->baseCommitDestroy))(uiControl(b));
}

static uintptr_t boxHandle(uiControl *c)
{
	struct box *b = (struct box *) c;

	return (uintptr_t) (b->widget);
}

static void boxContainerUpdateState(uiControl *c)
{
	struct box *b = (struct box *) c;
	struct boxControl *bc;
	uintmax_t i;

	for (i = 0; i < b->controls->len; i++) {
		bc = ptrArrayIndex(b->controls, struct boxControl *, i);
		uiControlUpdateState(bc->c);
	}
}

static void boxAppend(uiBox *ss, uiControl *c, int stretchy)
{
	struct box *b = (struct box *) ss;
	struct boxControl *bc;
	uintmax_t i;
	GtkWidget *widget;
	gboolean hexpand, vexpand;

	bc = uiNew(struct boxControl);
	bc->c = c;
	bc->stretchy = stretchy;
	widget = GTK_WIDGET(uiControlHandle(bc->c));
	hexpand = FALSE;
	vexpand = FALSE;
	if (bc->stretchy)
		if (b->vertical)
			vexpand = TRUE;
		else
			hexpand = TRUE;
	gtk_widget_set_hexpand(widget, hexpand);
	gtk_widget_set_halign(widget, GTK_ALIGN_FILL);
	gtk_widget_set_vexpand(widget, vexpand);
	gtk_widget_set_valign(widget, GTK_ALIGN_FILL);
	uiControlSetParent(bc->c, uiControl(b));
	ptrArrayAppend(b->controls, bc);
	uiControlQueueResize(uiControl(b));
	// TODO when adding boxInsertAt(), we use gtk_box_reorder_child()
}

static void boxDelete(uiBox *ss, uintmax_t index)
{
	struct box *b = (struct box *) ss;
	struct boxControl *bc;
	uiControl *removed;

	bc = ptrArrayIndex(b->controls, struct boxControl *, index);
	removed = bc->c;
	ptrArrayDelete(b->controls, index);
	uiControlSetParent(removed, NULL);
	uiFree(bc);
	uiControlQueueResize(uiControl(b));
}

static int boxPadded(uiBox *ss)
{
	struct box *b = (struct box *) ss;

	return b->padded;
}

static void boxSetPadded(uiBox *ss, int padded)
{
	struct box *b = (struct box *) ss;

	b->padded = padded;
	if (b->padded)
		if (b->vertical)
			gtk_box_set_spacing(b->box, gtkYPadding);
		else
			gtk_box_set_spacing(b->box, gtkXPadding);
	else
		gtk_box_set_spacing(b->box, 0);
	uiControlQueueResize(uiControl(b));
}

uiBox *finishNewBox(GtkOrientation orientation)
{
	struct box *b;

	b = (struct box *) uiNewControl(uiTypeBox());

	b->widget = gtk_box_new(orientation, 0);
	b->box = GTK_BOX(b->widget);
	uiUnixMakeSingleWidgetControl(uiControl(b), b->widget);

	b->controls = newPtrArray();

	b->vertical = orientation == GTK_ORIENTATION_VERTICAL;

	uiControl(b)->Handle = boxHandle;
	b->baseCommitDestroy = uiControl(b)->CommitDestroy;
	uiControl(b)->CommitDestroy = boxCommitDestroy;
	uiControl(b)->ContainerUpdateState = boxContainerUpdateState;

	uiBox(b)->Append = boxAppend;
	uiBox(b)->Delete = boxDelete;
	uiBox(b)->Padded = boxPadded;
	uiBox(b)->SetPadded = boxSetPadded;

	return uiBox(b);
}

uiBox *uiNewHorizontalBox(void)
{
	return finishNewBox(GTK_ORIENTATION_HORIZONTAL);
}

uiBox *uiNewVerticalBox(void)
{
	return finishNewBox(GTK_ORIENTATION_VERTICAL);
}