// 8 june 2016
#include "uipriv_unix.h"

struct formChild {
	uiControl *c;
	int stretchy;
	GtkWidget *label;
	gboolean oldhexpand;
	GtkAlign oldhalign;
	gboolean oldvexpand;
	GtkAlign oldvalign;
	GBinding *labelBinding;
};

struct uiForm {
	uiUnixControl c;
	GtkWidget *widget;
	GtkContainer *container;
	GtkGrid *grid;
	GArray *children;
	int padded;
	GtkSizeGroup *stretchygroup;		// ensures all stretchy controls have the same size
};

uiUnixControlAllDefaultsExceptDestroy(uiForm)

#define ctrl(f, i) &g_array_index(f->children, struct formChild, i)

static void uiFormDestroy(uiControl *c)
{
	uiForm *f = uiForm(c);
	struct formChild *fc;
	guint i;

	// kill the size group
	g_object_unref(f->stretchygroup);
	// free all controls
	for (i = 0; i < f->children->len; i++) {
		fc = ctrl(f, i);
		uiControlSetParent(fc->c, NULL);
		uiUnixControlSetContainer(uiUnixControl(fc->c), f->container, TRUE);
		uiControlDestroy(fc->c);
		gtk_widget_destroy(fc->label);
	}
	g_array_free(f->children, TRUE);
	// and then ourselves
	g_object_unref(f->widget);
	uiFreeControl(uiControl(f));
}

void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy)
{
	struct formChild fc;
	GtkWidget *widget;
	guint row;

	fc.c = c;
	widget = GTK_WIDGET(uiControlHandle(fc.c));
	fc.stretchy = stretchy;
	fc.oldhexpand = gtk_widget_get_hexpand(widget);
	fc.oldhalign = gtk_widget_get_halign(widget);
	fc.oldvexpand = gtk_widget_get_vexpand(widget);
	fc.oldvalign = gtk_widget_get_valign(widget);

	if (stretchy) {
		gtk_widget_set_vexpand(widget, TRUE);
		gtk_widget_set_valign(widget, GTK_ALIGN_FILL);
		gtk_size_group_add_widget(f->stretchygroup, widget);
	} else
		gtk_widget_set_vexpand(widget, FALSE);
	// and make them fill horizontally
	gtk_widget_set_hexpand(widget, TRUE);
	gtk_widget_set_halign(widget, GTK_ALIGN_FILL);

	fc.label = gtk_label_new(label);
	gtk_widget_set_hexpand(fc.label, FALSE);
	gtk_widget_set_halign(fc.label, GTK_ALIGN_END);
	gtk_widget_set_vexpand(fc.label, FALSE);
	if (GTK_IS_SCROLLED_WINDOW(widget))
		gtk_widget_set_valign(fc.label, GTK_ALIGN_START);
	else
		gtk_widget_set_valign(fc.label, GTK_ALIGN_CENTER);
	gtk_style_context_add_class(gtk_widget_get_style_context(fc.label), "dim-label");
	row = f->children->len;
	gtk_grid_attach(f->grid, fc.label,
		0, row,
		1, 1);
	// and make them share visibility so if the control is hidden, so is its label
	fc.labelBinding = g_object_bind_property(GTK_WIDGET(uiControlHandle(fc.c)), "visible",
		fc.label, "visible",
		G_BINDING_SYNC_CREATE);

	uiControlSetParent(fc.c, uiControl(f));
	uiUnixControlSetContainer(uiUnixControl(fc.c), f->container, FALSE);
	g_array_append_val(f->children, fc);

	// move the widget to the correct place
	gtk_container_child_set(f->container, widget,
		"left-attach", 1,
		"top-attach", row,
		NULL);
}

void uiFormDelete(uiForm *f, int index)
{
	struct formChild *fc;
	GtkWidget *widget;

	fc = ctrl(f, index);
	widget = GTK_WIDGET(uiControlHandle(fc->c));

	gtk_widget_destroy(fc->label);

	uiControlSetParent(fc->c, NULL);
	uiUnixControlSetContainer(uiUnixControl(fc->c), f->container, TRUE);

	if (fc->stretchy)
		gtk_size_group_remove_widget(f->stretchygroup, widget);
	gtk_widget_set_hexpand(widget, fc->oldhexpand);
	gtk_widget_set_halign(widget, fc->oldhalign);
	gtk_widget_set_vexpand(widget, fc->oldvexpand);
	gtk_widget_set_valign(widget, fc->oldvalign);

	g_array_remove_index(f->children, index);
}

int uiFormNumChildren(uiForm *f)
{
	return (int) f->children->len;
}

int uiFormPadded(uiForm *f)
{
	return f->padded;
}

void uiFormSetPadded(uiForm *f, int padded)
{
	f->padded = padded;
	if (f->padded) {
		gtk_grid_set_row_spacing(f->grid, uiprivGTKYPadding);
		gtk_grid_set_column_spacing(f->grid, uiprivGTKXPadding);
	} else {
		gtk_grid_set_row_spacing(f->grid, 0);
		gtk_grid_set_column_spacing(f->grid, 0);
	}
}

uiForm *uiNewForm(void)
{
	uiForm *f;

	uiUnixNewControl(uiForm, f);

	f->widget = gtk_grid_new();
	f->container = GTK_CONTAINER(f->widget);
	f->grid = GTK_GRID(f->widget);

	f->stretchygroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);

	f->children = g_array_new(FALSE, TRUE, sizeof (struct formChild));

	return f;
}
