#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "GtkDefs.h"

typedef GtkItemFactoryEntry* Gtk__ItemFactory__Entry;

static void default_ifactory_callback (gpointer callback_data, guint callback_action, GtkWidget *widget);


GtkItemFactoryEntry* 
SvGtkItemFactoryEntry(SV *data) {
	HV * h;
	AV * a;
	SV ** s;
	GtkItemFactoryEntry* e;
	STRLEN len;

	if ((!data) || (!SvOK(data)) || (!SvRV(data)) || 
			(SvTYPE(SvRV(data)) != SVt_PVHV && SvTYPE(SvRV(data)) != SVt_PVAV))
		return NULL;

	e = pgtk_alloc_temp(sizeof(GtkItemFactoryEntry));
	memset(e,0,sizeof(GtkItemFactoryEntry));

	/* path, accelerator, callback_action, item_type */
	if (SvTYPE(SvRV(data)) == SVt_PVHV) {
		h = (HV*)SvRV(data);
		if ((s=hv_fetch(h, "path", 4, 0)) && SvOK(*s))
			e->path = SvPV(*s, len);
		if ((s=hv_fetch(h, "accelerator", 11, 0)) && SvOK(*s))
			e->accelerator = SvPV(*s, len);
		if ((s=hv_fetch(h, "action", 6, 0)) && SvOK(*s))
			e->callback_action = SvIV(*s);
		if ((s=hv_fetch(h, "type", 4, 0)) && SvOK(*s))
			e->item_type = SvPV(*s, len);
	} else { /* array */
		a = (AV*)SvRV(data);
		if ((s=av_fetch(a, 0, 0)) && SvOK(*s))
			e->path = SvPV(*s, len);
		if ((s=av_fetch(a, 1, 0)) && SvOK(*s))
			e->accelerator = SvPV(*s, len);
		if ((s=av_fetch(a, 2, 0)) && SvOK(*s))
			e->callback_action = SvIV(*s);
		if ((s=av_fetch(a, 3, 0)) && SvOK(*s))
			e->item_type = SvPV(*s, len);
	}

	if (e->item_type && (!strcmp(e->item_type, "<Branch>") ||
			!strcmp(e->item_type, "<LastBranch>")) )
		e->callback = NULL;
	else
		e->callback = default_ifactory_callback;

	return e;
}

/* The GtkItemFactoryEntry struct does not have a separate
   callback_data member for each item.  Instead, the callback_data is
   passed into gtk_item_factory_create_item(s), and, to make matters
   even worse, gtk_item_factory_create_items() only has a single
   callback_data argument. Therefore, we have to get the actual
   callback data in a separate function from the one above. */

static SV*
ifactory_sv_get_handler(SV* data)
{
	SV* handler = NULL;

	if (SvTYPE(SvRV(data)) == SVt_PVHV) {
		HV *h = (HV*)SvRV(data);
		SV **s;
		if ((s = hv_fetch(h, "callback", 8, 0)) && SvOK(*s))
			handler = *s;
	} else if (SvTYPE(SvRV(data)) == SVt_PVAV) {
		AV *a = (AV*)SvRV(data);
		SV **s;
		if ((s = av_fetch(a, 4, 0)) && SvOK(*s))
			handler = *s;
	}
	return handler;
}

static void
default_ifactory_callback (gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	AV * args;
	SV * handler;
	SV * sv_widget;
	int i;

	dSP; 

	if (callback_data == NULL)
		return;

	PUSHMARK(SP);
	args = (AV*)callback_data;
	handler = *av_fetch(args, 0, 0); 
	sv_widget = newSVGtkObjectRef(GTK_OBJECT(widget), 0);

	XPUSHs(sv_2mortal(sv_widget));
	XPUSHs(sv_2mortal(newSViv(callback_action)));
	for (i=1;i<=av_len(args);i++)
		XPUSHs(sv_2mortal(newSVsv(*av_fetch(args, i, 0))));
	PUTBACK;

	perl_call_sv(handler, G_DISCARD);
}

MODULE = Gtk::ItemFactory	PACKAGE = Gtk::ItemFactory	PREFIX = gtk_item_factory_

#ifdef GTK_ITEM_FACTORY

Gtk::ItemFactory_Sink
gtk_item_factory_new(Class, container_type, path, accel_group)
	SV *		Class
	char*		container_type
	char*		path
	Gtk::AccelGroup	accel_group
	CODE:
	{
		GtkType wtype;
		wtype = gtnumber_for_gtname(container_type); 
		if (!wtype)
			wtype = gtnumber_for_ptname(container_type);
		RETVAL = (GtkItemFactory*)(gtk_item_factory_new(wtype, path, accel_group));
	}
	OUTPUT:
	RETVAL

void
gtk_item_factory_construct(item_factory, container_type, path, accel_group)
	Gtk::ItemFactory	item_factory
	char*		container_type
	char*		path
	Gtk::AccelGroup	accel_group
	CODE:
	{
		GtkType wtype;
		wtype = gtnumber_for_gtname(container_type); 
		if (!wtype)
			wtype = gtnumber_for_ptname(container_type);
		gtk_item_factory_construct(item_factory, wtype, path, accel_group);
	}

void
gtk_item_factory_parse_rc(Class, file_name)
	SV*	Class
	char*			file_name
	CODE:
	gtk_item_factory_parse_rc(file_name);

void
gtk_item_factory_parse_rc_string(Class, rc_string)
	SV*	Class
	char*			rc_string
	CODE:
	gtk_item_factory_parse_rc_string(rc_string);


#	gtk_item_factory_parse_rc_scanner()
#	gtk_item_factory_from_widget()

void
gtk_item_factory_add_foreign(Class, accel_widget, full_path, accel_group, keyval, modifiers)
	SV *	Class
	Gtk::Widget	accel_widget
	char *	full_path
	Gtk::AccelGroup	accel_group
	unsigned int	keyval
	Gtk::Gdk::ModifierType	modifiers
	CODE:
	gtk_item_factory_add_foreign(accel_widget, full_path, accel_group, keyval, modifiers);


Gtk::Widget_Up
gtk_item_factory_get_widget(item_factory, path)
	Gtk::ItemFactory	item_factory
	char*			path

Gtk::Widget_Up
gtk_item_factory_get_item(item_factory, path)
	Gtk::ItemFactory	item_factory
	char*			path

Gtk::Widget_Up
gtk_item_factory_get_widget_by_action(item_factory, action)
	Gtk::ItemFactory	item_factory
	unsigned int		action

Gtk::Widget_Up
gtk_item_factory_get_item_by_action(item_factory, action)
	Gtk::ItemFactory	item_factory
	unsigned int		action

void
gtk_item_factory_create_item(item_factory, entry, ...)
	Gtk::ItemFactory	item_factory
	Gtk::ItemFactory::Entry	entry
	CODE:
	{
		AV *args = NULL;
		/* Need to unref args when the item is destroyed.
		   Should be fixed at the Gtk+ level with a _full
		   version of the function.  */

		if (items > 2) {
			/* If we have a handler and arg-list, use it */
			args = newAV();
			PackCallbackST(args, 2);
		} else {
			SV *handler = ifactory_sv_get_handler(ST(1)); /* entry */
			if (handler) {
				args = newAV();
				PackCallback(args, handler);
			} else
				entry->callback = NULL;
		}
		gtk_item_factory_create_item(item_factory, entry, args, 1);
	}

void
gtk_item_factory_create_items(item_factory, ...)
	Gtk::ItemFactory	item_factory
	CODE:
	{
		int i;
		for (i = 1; i < items; i++) {
			GtkItemFactoryEntry *entry = SvGtkItemFactoryEntry(ST(i));
			SV *handler = ifactory_sv_get_handler(ST(i));
			AV *args = NULL;

			/* As above, args will leak memory :( */
			if (handler) {
				args = newAV();
				PackCallback(args, handler);
			} else
				entry->callback = NULL;
			gtk_item_factory_create_item(item_factory, entry, args, 1);
		}
	}

void
gtk_item_factory_delete_item(item_factory, path)
	Gtk::ItemFactory	item_factory
	char *	path

void
gtk_item_factory_delete_entry(item_factory, entry)
	Gtk::ItemFactory	item_factory
	Gtk::ItemFactory::Entry	entry

void
gtk_item_factory_popup(item_factory, x, y, mouse_button, time)
	Gtk::ItemFactory	item_factory
	unsigned int	x
	unsigned int	y
	unsigned int	mouse_button
	unsigned int	time

#	gtk_item_factory_set_translate_func

#endif

MODULE = Gtk::ItemFactory	PACKAGE = Gtk::Widget

#ifdef GTK_ITEM_FACTORY

Gtk::ItemFactory
item_factory(widget)
	Gtk::Widget	widget
	CODE:
	RETVAL = gtk_item_factory_from_widget (widget);
	OUTPUT:
	RETVAL

char*
item_factory_path(widget)
	Gtk::Widget	widget
	CODE:
	RETVAL = gtk_item_factory_path_from_widget (widget);
	OUTPUT:
	RETVAL

#endif