#include <glib.h>
#include <glib-object.h>


/* FIXME: GUINT_TO_POINTER() is not safe as GType can have larger
 *        size.  Change it.
 */


static GHashTable	*wrapper_types_ht = NULL;


GType
g_object_factory_get_wrapper_type (GType object_type)
{
  g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), G_TYPE_INVALID);

  if (!wrapper_types_ht)
    return object_type;
  else
    {
      GType  wrapper_type = GPOINTER_TO_UINT (g_hash_table_lookup (wrapper_types_ht,
								   GUINT_TO_POINTER (object_type)));

      return wrapper_type != G_TYPE_INVALID ? wrapper_type : object_type;
    }
}


void
g_object_factory_register_type (GType base_type,
				GType wrapper_type)
{
  g_return_if_fail (G_TYPE_IS_OBJECT (base_type));
  g_return_if_fail (G_TYPE_IS_OBJECT (wrapper_type));
  g_return_if_fail (!G_TYPE_IS_ABSTRACT (wrapper_type));
  g_return_if_fail (g_type_is_a (wrapper_type, base_type));

  /* OK, all basic test passed. */

  if (!wrapper_types_ht)
    wrapper_types_ht = g_hash_table_new (g_direct_hash, g_direct_equal);

  return g_hash_table_replace (wrapper_types_ht,
			       GUINT_TO_POINTER (base_type),
			       GUINT_TO_POINTER (wrapper_type));
}


GObject *
g_object_factory_new_valist (GType        object_type,
			     const gchar *first_property_name,
			     va_list      var_args)
{
  GObject *object;
  GType real_type;

  g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);

  return g_object_new_valist (g_object_factory_get_wrapper_type (object_type),
			      first_property_name, var_args);
}


GObject *
g_object_factory_new (GType        object_type,
		      const gchar *first_property_name,
		      ...)
{
  GObject *object;
  va_list var_args;

  g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);

  va_start (var_args, first_property_name);
  object = g_object_factory_new_valist (object_type, first_property_name, var_args);
  va_end (var_args);

  return object;
}



/* Testing it. */

#include <gtk/gtkentry.h>
#include <gtk/gtkspinbutton.h>

int
main (void)
{
  GObject *entry1;
  GObject *entry2;

  g_type_init ();

  entry1 = g_object_factory_new (GTK_TYPE_ENTRY, NULL);
  g_print ("%s\n", g_type_name (G_TYPE_FROM_INSTANCE (entry1)));

  /* This is just a hack to show how it is supposed to work.  Normally
   * you would register a custom entry subclass for that. */
  g_object_factory_register_type (GTK_TYPE_ENTRY, GTK_TYPE_SPIN_BUTTON);

  entry2 = g_object_factory_new (GTK_TYPE_ENTRY, NULL);
  g_print ("%s\n", g_type_name (G_TYPE_FROM_INSTANCE (entry2)));

  return 0;
}
