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

struct singleWidget {
	GtkWidget *widget;
	GtkWidget *scrolledWindow;
	GtkWidget *immediate;		// the widget that is added to the parent container; either widget or scrolledWindow
	uiContainer *parent;
	int hidden;
	void (*onDestroy)(void *);
	void *onDestroyData;
};

static void singleDestroy(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	if (s->parent != NULL)
		complain("attempt to destroy a uiControl at %p while it still has a parent", c);
	// first call the widget's own destruction code
	(*(s->onDestroy))(s->onDestroyData);
	// then actually destroy
	g_object_unref(s->immediate);
	// and free ourselves
	uiFree(s);
}

static uintptr_t singleHandle(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

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

static void singleSetParent(uiControl *c, uiContainer *parent)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);
	uiContainer *oldparent;
	GtkContainer *oldcontainer;
	GtkContainer *newcontainer;

	oldparent = s->parent;
	s->parent = parent;
	if (oldparent != NULL) {
		oldcontainer = GTK_CONTAINER(uiControlHandle(uiControl(oldparent)));
		gtk_container_remove(oldcontainer, s->immediate);
	}
	if (s->parent != NULL) {
		newcontainer = GTK_CONTAINER(uiControlHandle(uiControl(s->parent)));
		gtk_container_add(newcontainer, s->immediate);
	}
}

static void singlePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);
	GtkRequisition natural;

	// use the natural size as the minimum size is an *absolute* minimum
	// for example, if a label has ellipsizing on, it can be the width of the ellipses, not the text
	// there is a warning about height-for-width sizing, but in my tests this isn't an issue
	gtk_widget_get_preferred_size(s->widget, NULL, &natural);
	*width = natural.width;
	*height = natural.height;
}

static void singleResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);
	GtkAllocation a;

	a.x = x;
	a.y = y;
	a.width = width;
	a.height = height;
	gtk_widget_size_allocate(s->immediate, &a);
}

static int singleVisible(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	return !s->hidden;
}

static void singleShow(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	gtk_widget_show_all(s->immediate);
	s->hidden = 0;
	if (s->parent != NULL)
		uiContainerUpdate(s->parent);
}

static void singleHide(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	gtk_widget_hide(s->immediate);
	s->hidden = 1;
	if (s->parent != NULL)
		uiContainerUpdate(s->parent);
}

static void singleEnable(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	gtk_widget_set_sensitive(s->immediate, TRUE);
}

static void singleDisable(uiControl *c)
{
	struct singleWidget *s = (struct singleWidget *) (c->Internal);

	gtk_widget_set_sensitive(s->immediate, FALSE);
}

void uiUnixMakeControl(uiControl *c, GType type, gboolean inScrolledWindow, gboolean scrolledWindowHasBorder, void (*onDestroy)(void *), void *onDestroyData, const char *firstProperty, ...)
{
	struct singleWidget *s;
	va_list ap;

	s = uiNew(struct singleWidget);

	va_start(ap, firstProperty);
	s->widget = GTK_WIDGET(g_object_new_valist(type, firstProperty, ap));
	va_end(ap);
	s->immediate = s->widget;

	if (inScrolledWindow) {
		s->scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
		if (!GTK_IS_SCROLLABLE(s->widget))
			gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(s->scrolledWindow), s->widget);
		else
			gtk_container_add(GTK_CONTAINER(s->scrolledWindow), s->widget);
		if (scrolledWindowHasBorder)
			gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(s->scrolledWindow), GTK_SHADOW_IN);
		s->immediate = s->scrolledWindow;
	}

	// we need to keep an extra reference on the immediate widget
	// this is so we can reparent the control freely
	g_object_ref_sink(s->immediate);

	s->onDestroy = onDestroy;
	s->onDestroyData = onDestroyData;

	// finally, call gtk_widget_show_all() here to set the initial visibility of the widget
	gtk_widget_show_all(s->immediate);

	uiControl(c)->Internal = s;
	uiControl(c)->Destroy = singleDestroy;
	uiControl(c)->Handle = singleHandle;
	uiControl(c)->SetParent = singleSetParent;
	uiControl(c)->PreferredSize = singlePreferredSize;
	uiControl(c)->Resize = singleResize;
	uiControl(c)->Visible = singleVisible;
	uiControl(c)->Show = singleShow;
	uiControl(c)->Hide = singleHide;
	uiControl(c)->Enable = singleEnable;
	uiControl(c)->Disable = singleDisable;
}