/* -*- Mode: C; c-basic-offset: 4 -*-
 * pygtk- Python bindings for the GTK toolkit.
 * Copyright (C) 1998-2003  James Henstridge
 *
 *   pygtype.c: glue code to wrap the GType code.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include "pygobject-private.h"

/* -------------- __gtype__ objects ---------------------------- */

typedef struct {
    PyObject_HEAD
    GType type;
} PyGTypeWrapper;

static int
pyg_type_wrapper_compare(PyGTypeWrapper *self, PyGTypeWrapper *v)
{
    if (self->type == v->type) return 0;
    if (self->type > v->type) return -1;
    return 1;
}

static long
pyg_type_wrapper_hash(PyGTypeWrapper *self)
{
    return (long)self->type;
}

static PyObject *
pyg_type_wrapper_repr(PyGTypeWrapper *self)
{
    char buf[80];
    const gchar *name = g_type_name(self->type);

    g_snprintf(buf, sizeof(buf), "<GType %s (%lu)>",
	       name?name:"invalid", (unsigned long int) self->type);
    return PyString_FromString(buf);
}

static void
pyg_type_wrapper_dealloc(PyGTypeWrapper *self)
{
    PyObject_DEL(self);
}

static PyObject *
_wrap_g_type_wrapper__get_pytype(PyGTypeWrapper *self, void *closure)
{
    PyObject *py_type;
    
    py_type = g_type_get_qdata(self->type, pygobject_class_key);
    if (!py_type)
      py_type = Py_None;

    Py_INCREF(py_type);
    return py_type;
}

static PyObject *
_wrap_g_type_wrapper__get_name(PyGTypeWrapper *self, void *closure)
{
   const char *name = g_type_name(self->type);
   return PyString_FromString(g_strdup(name ? name : "invalid"));
}

static PyObject *
_wrap_g_type_wrapper__get_parent(PyGTypeWrapper *self, void *closure)
{
   return pyg_type_wrapper_new(g_type_parent(self->type));
}

static PyObject *
_wrap_g_type_wrapper__get_fundamental(PyGTypeWrapper *self, void *closure)
{
   return pyg_type_wrapper_new(g_type_fundamental(self->type));
}

static PyObject *
_wrap_g_type_wrapper__get_children(PyGTypeWrapper *self, void *closure)
{
  guint n_children, i;
  GType *children;
  PyObject *retval;
    
  children = g_type_children(self->type, &n_children);

  retval = PyList_New(n_children);
  for (i = 0; i < n_children; i++)
      PyList_SetItem(retval, i, pyg_type_wrapper_new(children[i]));
  g_free(children);
  
  return retval;
}

static PyObject *
_wrap_g_type_wrapper__get_interfaces(PyGTypeWrapper *self, void *closure)
{
  guint n_interfaces, i;
  GType *interfaces;
  PyObject *retval;
    
  interfaces = g_type_interfaces(self->type, &n_interfaces);

  retval = PyList_New(n_interfaces);
  for (i = 0; i < n_interfaces; i++)
      PyList_SetItem(retval, i, pyg_type_wrapper_new(interfaces[i]));
  g_free(interfaces);
  
  return retval;
}

static PyObject *
_wrap_g_type_wrapper__get_depth(PyGTypeWrapper *self, void *closure)
{
  return PyInt_FromLong(g_type_depth(self->type));
}

static PyGetSetDef _PyGTypeWrapper_getsets[] = {
    { "pytype", (getter)_wrap_g_type_wrapper__get_pytype, (setter)0 },
    { "name",  (getter)_wrap_g_type_wrapper__get_name, (setter)0 },
    { "fundamental",  (getter)_wrap_g_type_wrapper__get_fundamental, (setter)0 },
    { "parent",  (getter)_wrap_g_type_wrapper__get_parent, (setter)0 },
    { "children",  (getter)_wrap_g_type_wrapper__get_children, (setter)0 },
    { "interfaces",  (getter)_wrap_g_type_wrapper__get_interfaces, (setter)0 },
    { "depth",  (getter)_wrap_g_type_wrapper__get_depth, (setter)0 },
    { NULL, (getter)0, (setter)0 }
};

static PyObject*
_wrap_g_type_is_interface(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_INTERFACE(self->type));
}

static PyObject*
_wrap_g_type_is_classed(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_CLASSED(self->type));
}

static PyObject*
_wrap_g_type_is_instantiatable(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_INSTANTIATABLE(self->type));
}

static PyObject*
_wrap_g_type_is_derivable(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_DERIVABLE(self->type));
}

static PyObject*
_wrap_g_type_is_deep_derivable(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_DEEP_DERIVABLE(self->type));
}

static PyObject*
_wrap_g_type_is_abstract(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_ABSTRACT(self->type));
}

static PyObject*
_wrap_g_type_is_value_abstract(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_VALUE_ABSTRACT(self->type));
}

static PyObject*
_wrap_g_type_is_value_type(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_IS_VALUE_TYPE(self->type));
}

static PyObject*
_wrap_g_type_has_value_table(PyGTypeWrapper *self)
{
    return PyBool_FromLong(G_TYPE_HAS_VALUE_TABLE(self->type));
}

static PyObject*
_wrap_g_type_from_name(PyGTypeWrapper *_, PyObject *args)
{
    char *type_name;
    GType type;
    
    if (!PyArg_ParseTuple(args, "s:GType.from_name", &type_name))
	return NULL;

    type = _pyg_type_from_name(type_name);
    if (type == 0) {
	PyErr_SetString(PyExc_RuntimeError, "unknown type name");
	return NULL;
    }

    return pyg_type_wrapper_new(type);
}

static PyObject*
_wrap_g_type_is_a(PyGTypeWrapper *self, PyObject *args)
{
    PyObject *gparent;
    GType parent;

    if (!PyArg_ParseTuple(args, "O:GType.is_a", &gparent))
	return NULL;
    else if ((parent = pyg_type_from_object(gparent)) == 0)
	return NULL;
    
    return PyBool_FromLong(g_type_is_a(self->type, parent));
}

static PyMethodDef _PyGTypeWrapper_methods[] = {
    { "is_interface", (PyCFunction)_wrap_g_type_is_interface, METH_NOARGS },
    { "is_classed", (PyCFunction)_wrap_g_type_is_classed, METH_NOARGS },
    { "is_instantiatable", (PyCFunction)_wrap_g_type_is_instantiatable, METH_NOARGS },
    { "is_derivable", (PyCFunction)_wrap_g_type_is_derivable, METH_NOARGS },
    { "is_deep_derivable", (PyCFunction)_wrap_g_type_is_deep_derivable, METH_NOARGS },
    { "is_abstract", (PyCFunction)_wrap_g_type_is_abstract, METH_NOARGS },
    { "is_value_abstract", (PyCFunction)_wrap_g_type_is_value_abstract, METH_NOARGS },
    { "is_value_type", (PyCFunction)_wrap_g_type_is_value_type, METH_NOARGS },
    { "has_value_table", (PyCFunction)_wrap_g_type_has_value_table, METH_NOARGS },
    { "from_name", (PyCFunction)_wrap_g_type_from_name, METH_VARARGS | METH_STATIC },
    { "is_a", (PyCFunction)_wrap_g_type_is_a, METH_VARARGS },
    { NULL,  0, 0 }
};
    
static int
pyg_type_wrapper_init(PyGTypeWrapper *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = { "object", NULL };
    PyObject *py_object;
    GType type;
    
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
				     "O:GType.__init__",
				     kwlist, &py_object))
        return -1;

    if (!(type = pyg_type_from_object(py_object)))
	return -1;

    self->type = type;

    return 0;
}

PyTypeObject PyGTypeWrapper_Type = {
    PyObject_HEAD_INIT(NULL)
    0,
    "gobject.GType",
    sizeof(PyGTypeWrapper),
    0,
    (destructor)pyg_type_wrapper_dealloc,
    (printfunc)0,
    (getattrfunc)0,
    (setattrfunc)0,
    (cmpfunc)pyg_type_wrapper_compare,
    (reprfunc)pyg_type_wrapper_repr,
    0,
    0,
    0,
    (hashfunc)pyg_type_wrapper_hash,
    (ternaryfunc)0,
    (reprfunc)0,
    (getattrofunc)0,		
    (setattrofunc)0,		
    0,				
    Py_TPFLAGS_DEFAULT, 
    NULL,
    (traverseproc)0,		
    (inquiry)0,
    (richcmpfunc)0,	
    0,             
    (getiterfunc)0,
    (iternextfunc)0,
    _PyGTypeWrapper_methods,
    0,
    _PyGTypeWrapper_getsets,
    NULL,		
    NULL,		
    (descrgetfunc)0,	
    (descrsetfunc)0,	
    0,                 
    (initproc)pyg_type_wrapper_init
};

/**
 * pyg_type_wrapper_new:
 * type: a GType
 *
 * Creates a Python wrapper for a GType.
 *
 * Returns: the Python wrapper.
 */
PyObject *
pyg_type_wrapper_new(GType type)
{
    PyGTypeWrapper *self;

    self = (PyGTypeWrapper *)PyObject_NEW(PyGTypeWrapper,
					  &PyGTypeWrapper_Type);
    if (self == NULL)
	return NULL;

    self->type = type;
    return (PyObject *)self;
}

/**
 * pyg_type_from_object:
 * obj: a Python object
 *
 * converts a python object to a GType.  Raises an exception if it
 * can't perform the conversion.
 *
 * Returns: the corresponding GType, or 0 on error.
 */
GType
pyg_type_from_object(PyObject *obj)
{
    PyObject *gtype;
    GType type;

    /* NULL check */
    if (!obj) {
	PyErr_SetString(PyExc_TypeError, "can't get type from NULL object");
	return 0;
    }

    /* map some standard types to primitive GTypes ... */
    if (obj == Py_None)
	return G_TYPE_NONE;
    if (PyType_Check(obj)) {
	PyTypeObject *tp = (PyTypeObject *)obj;

	if (tp == &PyInt_Type)
	    return G_TYPE_INT;
	else if (tp == &PyBool_Type)
	    return G_TYPE_BOOLEAN;
	else if (tp == &PyLong_Type)
	    return G_TYPE_LONG;
	else if (tp == &PyFloat_Type)
	    return G_TYPE_DOUBLE;
	else if (tp == &PyString_Type)
	    return G_TYPE_STRING;
	else if (tp == &PyBaseObject_Type)
	    return PY_TYPE_OBJECT;
    }

    if (obj->ob_type == &PyGTypeWrapper_Type) {
	return ((PyGTypeWrapper *)obj)->type;
    }

    /* handle strings */
    if (PyString_Check(obj)) {
	gchar *name = PyString_AsString(obj);
	
	type = _pyg_type_from_name(name);
	if (type != 0) {
	    return type;
	}
    }

    /* finally, look for a __gtype__ attribute on the object */
    gtype = PyObject_GetAttrString(obj, "__gtype__");

    if (gtype) {
	if (gtype->ob_type == &PyGTypeWrapper_Type) {
	    type = ((PyGTypeWrapper *)gtype)->type;
	    Py_DECREF(gtype);
	    return type;
	}
	Py_DECREF(gtype);
    }

    PyErr_Clear();
    PyErr_SetString(PyExc_TypeError, "could not get typecode from object");
    return 0;
}

/* -------------- GValue marshalling ------------------ */

/**
 * pyg_enum_get_value:
 * @enum_type: the GType of the flag.
 * @obj: a Python object representing the flag value
 * @val: a pointer to the location to store the integer representation of the flag.
 *
 * Converts a Python object to the integer equivalent.  The conversion
 * will depend on the type of the Python object.  If the object is an
 * integer, it is passed through directly.  If it is a string, it will
 * be treated as a full or short enum name as defined in the GType.
 *
 * Returns: 0 on success or -1 on failure
 */
gint
pyg_enum_get_value(GType enum_type, PyObject *obj, gint *val)
{
    GEnumClass *eclass = NULL;
    gint res = -1;

    g_return_val_if_fail(val != NULL, -1);
    if (!obj) {
	*val = 0;
	res = 0;
    } else if (PyInt_Check(obj)) {
	*val = PyInt_AsLong(obj);
	res = 0;
    } else if (PyString_Check(obj)) {
	GEnumValue *info;
	char *str = PyString_AsString(obj);
	
	if (enum_type != G_TYPE_NONE)
	    eclass = G_ENUM_CLASS(g_type_class_ref(enum_type));
	else {
	    PyErr_SetString(PyExc_TypeError, "could not convert string to enum because there is no GType associated to look up the value");
	    res = -1;
	}
	info = g_enum_get_value_by_name(eclass, str);
	g_type_class_unref(eclass);

	if (!info)
	    info = g_enum_get_value_by_nick(eclass, str);
	if (info) {
	    *val = info->value;
	    res = 0;
	} else {
	    PyErr_SetString(PyExc_TypeError, "could not convert string");
	    res = -1;
	}
    } else {
	PyErr_SetString(PyExc_TypeError,"enum values must be strings or ints");
	res = -1;
    }
    return res;
}

/**
 * pyg_flags_get_value:
 * @flag_type: the GType of the flag.
 * @obj: a Python object representing the flag value
 * @val: a pointer to the location to store the integer representation of the flag.
 *
 * Converts a Python object to the integer equivalent.  The conversion
 * will depend on the type of the Python object.  If the object is an
 * integer, it is passed through directly.  If it is a string, it will
 * be treated as a full or short flag name as defined in the GType.
 * If it is a tuple, then the items are treated as strings and ORed
 * together.
 *
 * Returns: 0 on success or -1 on failure
 */
gint
pyg_flags_get_value(GType flag_type, PyObject *obj, gint *val)
{
    GFlagsClass *fclass = NULL;
    gint res = -1;

    g_return_val_if_fail(val != NULL, -1);
    if (!obj) {
	*val = 0;
	res = 0;
    } else if (PyInt_Check(obj)) {
	*val = PyInt_AsLong(obj);
	res = 0;
    } else if (PyLong_Check(obj)) {
        *val = PyLong_AsLongLong(obj);
        res = 0;
    } else if (PyString_Check(obj)) {
	GFlagsValue *info;
	char *str = PyString_AsString(obj);

	if (flag_type != G_TYPE_NONE)
	    fclass = G_FLAGS_CLASS(g_type_class_ref(flag_type));
	else {
	    PyErr_SetString(PyExc_TypeError, "could not convert string to flag because there is no GType associated to look up the value");
	    res = -1;
	}
	info = g_flags_get_value_by_name(fclass, str);
	g_type_class_unref(fclass);
	
	if (!info)
	    info = g_flags_get_value_by_nick(fclass, str);
	if (info) {
	    *val = info->value;
	    res = 0;
	} else {
	    PyErr_SetString(PyExc_TypeError, "could not convert string");
	    res = -1;
	}
    } else if (PyTuple_Check(obj)) {
	int i, len;

	len = PyTuple_Size(obj);
	*val = 0;
	res = 0;

	if (flag_type != G_TYPE_NONE)
	    fclass = G_FLAGS_CLASS(g_type_class_ref(flag_type));
	else {
	    PyErr_SetString(PyExc_TypeError, "could not convert string to flag because there is no GType associated to look up the value");
	    res = -1;
	}

	for (i = 0; i < len; i++) {
	    PyObject *item = PyTuple_GetItem(obj, i);
	    char *str = PyString_AsString(item);
	    GFlagsValue *info = g_flags_get_value_by_name(fclass, str);

	    if (!info)
		info = g_flags_get_value_by_nick(fclass, str);
	    if (info) {
		*val |= info->value;
	    } else {
		PyErr_SetString(PyExc_TypeError, "could not convert string");
		res = -1;
		break;
	    }
	}
	g_type_class_unref(fclass);
    } else {
	PyErr_SetString(PyExc_TypeError,
			"flag values must be strings, ints, longs, or tuples");
	res = -1;
    }
    return res;
}

typedef struct {
    fromvaluefunc fromvalue;
    tovaluefunc tovalue;
} PyGTypeMarshal;
static GQuark pyg_type_marshal_key = 0;

static PyGTypeMarshal *
pyg_type_lookup(GType type)
{
    GType	ptype = type;
    PyGTypeMarshal	*tm = NULL;

    /* recursively lookup types */
    while (ptype) {
	if ((tm = g_type_get_qdata(ptype, pyg_type_marshal_key)) != NULL)
	    break;
	ptype = g_type_parent(ptype);
    }
    return tm;
}

/**
 * pyg_register_gtype_custom:
 * @gtype: the GType for the new type
 * @from_func: a function to convert GValues to Python objects
 * @to_func: a function to convert Python objects to GValues
 *
 * In order to handle specific conversion of gboxed types or new
 * fundamental types, you may use this function to register conversion
 * handlers.
 */

void
pyg_register_gtype_custom(GType gtype,
			  fromvaluefunc from_func,
                          tovaluefunc to_func)
{
    PyGTypeMarshal *tm;

    if (!pyg_type_marshal_key)
        pyg_type_marshal_key = g_quark_from_static_string("PyGType::marshal");

    tm = g_new(PyGTypeMarshal, 1);
    tm->fromvalue = from_func;
    tm->tovalue = to_func;
    g_type_set_qdata(gtype, pyg_type_marshal_key, tm);
}

static int
pyg_value_array_from_pyobject(GValue *value,
			      PyObject *obj,
			      const GParamSpecValueArray *pspec)
{
    int len;
    GValueArray *value_array;
    int i;

    len = PySequence_Length(obj);
    if (len == -1) {
	PyErr_Clear();
	return -1;
    }

    if (pspec && pspec->fixed_n_elements > 0 && len != pspec->fixed_n_elements)
	return -1;
	    
    value_array = g_value_array_new(len);

    for (i = 0; i < len; ++i) {
	PyObject *item = PySequence_GetItem(obj, i);
	GType type;
	GValue item_value = { 0, };
	int status;

	if (! item) {
	    PyErr_Clear();
	    g_value_array_free(value_array);
	    return -1;
	}

	if (pspec && pspec->element_spec)
	    type = G_PARAM_SPEC_VALUE_TYPE(pspec->element_spec);
	else if (item == Py_None)
	    type = G_TYPE_POINTER; /* store None as NULL */
	else {
	    type = pyg_type_from_object((PyObject *) item->ob_type);
	    if (! type) {
		PyErr_Clear();
		g_value_array_free(value_array);
		Py_DECREF(item);
		return -1;
	    }
	}

	g_value_init(&item_value, type);
	status = (pspec && pspec->element_spec)
	    ? pyg_param_gvalue_from_pyobject(&item_value, item, pspec->element_spec)
	    : pyg_value_from_pyobject(&item_value, item);
	Py_DECREF(item);

	if (status == -1) {
	    g_value_array_free(value_array);
	    g_value_unset(&item_value);
	    return -1;
	}

	g_value_array_append(value_array, &item_value);
	g_value_unset(&item_value);
    }

    g_value_take_boxed(value, value_array);
    return 0;
}

/**
 * pyg_value_from_pyobject:
 * @value: the GValue object to store the converted value in.
 * @obj: the Python object to convert.
 *
 * This function converts a Python object and stores the result in a
 * GValue.  The GValue must be initialised in advance with
 * g_value_init().  If the Python object can't be converted to the
 * type of the GValue, then an error is returned.
 *
 * Returns: 0 on success, -1 on error.
 */
int
pyg_value_from_pyobject(GValue *value, PyObject *obj)
{
    PyObject *tmp;

    switch (G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value))) {
    case G_TYPE_INTERFACE:
	/* we only handle interface types that have a GObject prereq */
	if (g_type_is_a(G_VALUE_TYPE(value), G_TYPE_OBJECT)) {
	    if (!PyObject_TypeCheck(obj, &PyGObject_Type)) {
		return -1;
	    }
	    if (!G_TYPE_CHECK_INSTANCE_TYPE(pygobject_get(obj),
					    G_VALUE_TYPE(value))) {
		return -1;
	    }
	    g_value_set_object(value, pygobject_get(obj));
	} else {
	    return -1;
	}
	break;
    case G_TYPE_CHAR:
	if ((tmp = PyObject_Str(obj)))
	    g_value_set_char(value, PyString_AsString(tmp)[0]);
	else {
	    PyErr_Clear();
	    return -1;
	}
	Py_DECREF(tmp);
	break;
    case G_TYPE_UCHAR:
	if ((tmp = PyObject_Str(obj)))
	    g_value_set_char(value, PyString_AsString(tmp)[0]);
	else {
	    PyErr_Clear();
	    return -1;
	}
	Py_DECREF(tmp);
	break;
    case G_TYPE_BOOLEAN:
	g_value_set_boolean(value, PyObject_IsTrue(obj));
	break;
    case G_TYPE_INT:
	g_value_set_int(value, PyInt_AsLong(obj));
	if (PyErr_Occurred()) {
	    g_value_unset(value);
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_UINT:
	{
	    if (PyInt_Check(obj)) {
		glong val;

		val = PyInt_AsLong(obj);
		if (val >= 0 && val <= G_MAXUINT)
		    g_value_set_uint(value, (guint)val);
		else
		    return -1;
	    } else {
		g_value_set_uint(value, PyLong_AsUnsignedLong(obj));
		if (PyErr_Occurred()) {
		    g_value_unset(value);
		    PyErr_Clear();
		    return -1;
		}
	    }
	}
	break;
    case G_TYPE_LONG:
	g_value_set_long(value, PyInt_AsLong(obj));
	if (PyErr_Occurred()) {
	    g_value_unset(value);
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_ULONG:
	{
	    if (PyInt_Check(obj)) {
		glong val;

		val = PyInt_AsLong(obj);
		if (val >= 0)
		    g_value_set_ulong(value, (gulong)val);
		else
		    return -1;
	    } else {
		g_value_set_ulong(value, PyLong_AsUnsignedLong(obj));
		if (PyErr_Occurred()) {
		    g_value_unset(value);
		    PyErr_Clear();
		    return -1;
		}
	    }
	}
	break;
    case G_TYPE_INT64:
	g_value_set_int64(value, PyLong_AsLongLong(obj));
	if (PyErr_Occurred()) {
	    g_value_unset(value);
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_UINT64:
        if (PyInt_Check(obj))
            g_value_set_uint64(value, PyInt_AsLong(obj));
        else if (PyLong_Check(obj))
            g_value_set_uint64(value, PyLong_AsUnsignedLongLong(obj));
        else
            return -1;
	break;
    case G_TYPE_ENUM:
	{
	    gint val = 0;
	    if (pyg_enum_get_value(G_VALUE_TYPE(value), obj, &val) < 0) {
		PyErr_Clear();
		return -1;
	    }
	    g_value_set_enum(value, val);
	}
	break;
    case G_TYPE_FLAGS:
	{
	    gint val = 0;
	    if (pyg_flags_get_value(G_VALUE_TYPE(value), obj, &val) < 0) {
		PyErr_Clear();
		return -1;
	    }
	    g_value_set_flags(value, val);
	}
	break;
    case G_TYPE_FLOAT:
	g_value_set_float(value, PyFloat_AsDouble(obj));
	if (PyErr_Occurred()) {
	    g_value_unset(value);
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_DOUBLE:
	g_value_set_double(value, PyFloat_AsDouble(obj));
	if (PyErr_Occurred()) {
	    g_value_unset(value);
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_STRING:
	if (obj == Py_None)
	    g_value_set_string(value, NULL);
	else if ((tmp = PyObject_Str(obj))) {
	    g_value_set_string(value, PyString_AsString(tmp));
	    Py_DECREF(tmp);
	} else {
	    PyErr_Clear();
	    return -1;
	}
	break;
    case G_TYPE_POINTER:
	if (obj == Py_None)
	    g_value_set_pointer(value, NULL);
	else if (PyObject_TypeCheck(obj, &PyGPointer_Type) &&
		   G_VALUE_HOLDS(value, ((PyGPointer *)obj)->gtype))
	    g_value_set_pointer(value, pyg_pointer_get(obj, gpointer));
	else if (PyCObject_Check(obj))
	    g_value_set_pointer(value, PyCObject_AsVoidPtr(obj));
	else
	    return -1;
	break;
    case G_TYPE_BOXED: {
	PyGTypeMarshal *bm;

	if (obj == Py_None)
	    g_value_set_boxed(value, NULL);
	else if (G_VALUE_HOLDS(value, PY_TYPE_OBJECT))
	    g_value_set_boxed(value, obj);
	else if (PyObject_TypeCheck(obj, &PyGBoxed_Type) &&
		   G_VALUE_HOLDS(value, ((PyGBoxed *)obj)->gtype))
	    g_value_set_boxed(value, pyg_boxed_get(obj, gpointer));
        else if (G_VALUE_HOLDS(value, G_TYPE_VALUE)) {
            GType type;
            GValue *n_value; 
            
            type = pyg_type_from_object((PyObject *)obj->ob_type);
            if (G_UNLIKELY (! type)) {
                PyErr_Clear();
                return -1;
            }
            n_value = g_new0 (GValue, 1);
            g_value_init (n_value, type);
            g_value_take_boxed (value, n_value);
            return pyg_value_from_pyobject (n_value, obj);
        }
        else if (PySequence_Check(obj) &&
		   G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY))
	    return pyg_value_array_from_pyobject(value, obj, NULL);
	else if (PyString_Check(obj) &&
                 G_VALUE_HOLDS(value, G_TYPE_GSTRING)) {
            GString *string;
            char *buffer;
            Py_ssize_t len;
            if (PyString_AsStringAndSize(obj, &buffer, &len))
                return -1;
            string = g_string_new_len(buffer, len);
	    g_value_set_boxed(value, string);
            break;
        }
	else if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))) != NULL)
	    return bm->tovalue(value, obj);
	else if (PyCObject_Check(obj))
	    g_value_set_boxed(value, PyCObject_AsVoidPtr(obj));
	else
	    return -1;
	break;
    }
    case G_TYPE_PARAM:
	if (PyGParamSpec_Check(obj))
	    g_value_set_param(value, PyCObject_AsVoidPtr(obj));
	else
	    return -1;
	break;
    case G_TYPE_OBJECT:
	if (obj == Py_None) {
	    g_value_set_object(value, NULL);
	} else if (PyObject_TypeCheck(obj, &PyGObject_Type) &&
		   G_TYPE_CHECK_INSTANCE_TYPE(pygobject_get(obj),
					      G_VALUE_TYPE(value))) {
	    g_value_set_object(value, pygobject_get(obj));
	} else
	    return -1;
	break;
    default:
	{
	    PyGTypeMarshal *bm;
	    if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))) != NULL)
		return bm->tovalue(value, obj);
	    break;
	}
    }
    return 0;
}

/**
 * pyg_value_as_pyobject:
 * @value: the GValue object.
 * @copy_boxed: true if boxed values should be copied.
 *
 * This function creates/returns a Python wrapper object that
 * represents the GValue passed as an argument.
 *
 * Returns: a PyObject representing the value.
 */
PyObject *
pyg_value_as_pyobject(const GValue *value, gboolean copy_boxed)
{
    gchar buf[128];

    switch (G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value))) {
    case G_TYPE_INTERFACE:
	if (g_type_is_a(G_VALUE_TYPE(value), G_TYPE_OBJECT))
	    return pygobject_new(g_value_get_object(value));
	else
	    break;
    case G_TYPE_CHAR: {
	gint8 val = g_value_get_char(value);
	return PyString_FromStringAndSize((char *)&val, 1);
    }
    case G_TYPE_UCHAR: {
	guint8 val = g_value_get_uchar(value);
	return PyString_FromStringAndSize((char *)&val, 1);
    }
    case G_TYPE_BOOLEAN: {
	return PyBool_FromLong(g_value_get_boolean(value));
    }
    case G_TYPE_INT:
	return PyInt_FromLong(g_value_get_int(value));
    case G_TYPE_UINT:
	{
	    /* in Python, the Int object is backed by a long.  If a
	       long can hold the whole value of an unsigned int, use
	       an Int.  Otherwise, use a Long object to avoid overflow.
	       This matches the ULongArg behavior in codegen/argtypes.h */
#if (G_MAXUINT <= G_MAXLONG)
	    return PyInt_FromLong((glong) g_value_get_uint(value));
#else
	    return PyLong_FromUnsignedLong((gulong) g_value_get_uint(value));
#endif
	}
    case G_TYPE_LONG:
	return PyInt_FromLong(g_value_get_long(value));
    case G_TYPE_ULONG:
	{
	    gulong val = g_value_get_ulong(value);

	    if (val <= G_MAXLONG)
		return PyInt_FromLong((glong) val);
	    else
		return PyLong_FromUnsignedLong(val);
	}
    case G_TYPE_INT64:
	{
	    gint64 val = g_value_get_int64(value);

	    if (G_MINLONG <= val && val <= G_MAXLONG)
		return PyInt_FromLong((glong) val);
	    else
		return PyLong_FromLongLong(val);
	}
    case G_TYPE_UINT64:
	{
	    guint64 val = g_value_get_uint64(value);

	    if (val <= G_MAXLONG)
		return PyInt_FromLong((glong) val);
	    else
		return PyLong_FromUnsignedLongLong(val);
	}
    case G_TYPE_ENUM:
	return pyg_enum_from_gtype(G_VALUE_TYPE(value), g_value_get_enum(value));
    case G_TYPE_FLAGS:
	return pyg_flags_from_gtype(G_VALUE_TYPE(value), g_value_get_flags(value));
    case G_TYPE_FLOAT:
	return PyFloat_FromDouble(g_value_get_float(value));
    case G_TYPE_DOUBLE:
	return PyFloat_FromDouble(g_value_get_double(value));
    case G_TYPE_STRING:
	{
	    const gchar *str = g_value_get_string(value);

	    if (str)
		return PyString_FromString(str);
	    Py_INCREF(Py_None);
	    return Py_None;
	}
    case G_TYPE_POINTER:
	return pyg_pointer_new(G_VALUE_TYPE(value),
			       g_value_get_pointer(value));
    case G_TYPE_BOXED: {
	PyGTypeMarshal *bm;

	if (G_VALUE_HOLDS(value, PY_TYPE_OBJECT)) {
	    PyObject *ret = (PyObject *)g_value_dup_boxed(value);
	    if (ret == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	    }
	    return ret;
        } else if (G_VALUE_HOLDS(value, G_TYPE_VALUE)) {
            GValue *n_value = g_value_get_boxed (value);
            return pyg_value_as_pyobject(n_value, copy_boxed);
        } else if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY)) {
	    GValueArray *array = (GValueArray *) g_value_get_boxed(value);
	    PyObject *ret = PyList_New(array->n_values);
	    int i;
	    for (i = 0; i < array->n_values; ++i)
		PyList_SET_ITEM(ret, i, pyg_value_as_pyobject
                                (array->values + i, copy_boxed));
	    return ret;
	} else if (G_VALUE_HOLDS(value, G_TYPE_GSTRING)) {
	    GString *string = (GString *) g_value_get_boxed(value);
	    PyObject *ret = PyString_FromStringAndSize(string->str, string->len);
	    return ret;
	}	    
	bm = pyg_type_lookup(G_VALUE_TYPE(value));
	if (bm) {
	    return bm->fromvalue(value);
	} else {
	    if (copy_boxed)
		return pyg_boxed_new(G_VALUE_TYPE(value),
				     g_value_get_boxed(value), TRUE, TRUE);
	    else
		return pyg_boxed_new(G_VALUE_TYPE(value),
				     g_value_get_boxed(value),FALSE,FALSE);
	}
    }
    case G_TYPE_PARAM:
	return pyg_param_spec_new(g_value_get_param(value));
    case G_TYPE_OBJECT:
	return pygobject_new(g_value_get_object(value));
    default:
	{
	    PyGTypeMarshal *bm;
	    if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))))
		return bm->fromvalue(value);
	    break;
	}
    }
    g_snprintf(buf, sizeof(buf), "unknown type %s",
	       g_type_name(G_VALUE_TYPE(value)));
    PyErr_SetString(PyExc_TypeError, buf);
    return NULL;
}

/* -------------- PyGClosure ----------------- */

static void
pyg_closure_invalidate(gpointer data, GClosure *closure)
{
    PyGClosure *pc = (PyGClosure *)closure;
    PyGILState_STATE state;

    state = pyg_gil_state_ensure();
    Py_XDECREF(pc->callback);
    Py_XDECREF(pc->extra_args);
    Py_XDECREF(pc->swap_data);
    pyg_gil_state_release(state);

    pc->callback = NULL;
    pc->extra_args = NULL;
    pc->swap_data = NULL;
}

static void
pyg_closure_marshal(GClosure *closure,
		    GValue *return_value,
		    guint n_param_values,
		    const GValue *param_values,
		    gpointer invocation_hint,
		    gpointer marshal_data)
{
    PyGILState_STATE state;
    PyGClosure *pc = (PyGClosure *)closure;
    PyObject *params, *ret;
    guint i;

    state = pyg_gil_state_ensure();

    /* construct Python tuple for the parameter values */
    params = PyTuple_New(n_param_values);
    for (i = 0; i < n_param_values; i++) {
	/* swap in a different initial data for connect_object() */
	if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
	    g_return_if_fail(pc->swap_data != NULL);
	    Py_INCREF(pc->swap_data);
	    PyTuple_SetItem(params, 0, pc->swap_data);
	} else {
	    PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);

	    /* error condition */
	    if (!item) {
		goto out;
	    }
	    PyTuple_SetItem(params, i, item);
	}
    }
    /* params passed to function may have extra arguments */
    if (pc->extra_args) {
	PyObject *tuple = params;
	params = PySequence_Concat(tuple, pc->extra_args);
	Py_DECREF(tuple);
    }
    ret = PyObject_CallObject(pc->callback, params);
    if (ret == NULL) {
	if (pc->exception_handler)
	    pc->exception_handler(return_value, n_param_values, param_values);
	else
	    PyErr_Print();
	goto out;
    }
    
    if (return_value && pyg_value_from_pyobject(return_value, ret) != 0) {
	PyErr_SetString(PyExc_TypeError,
			"can't convert return value to desired type");
	
	if (pc->exception_handler)
	    pc->exception_handler(return_value, n_param_values, param_values);
	else
	    PyErr_Print();
    }
    Py_DECREF(ret);
    
 out:
    Py_DECREF(params);
    pyg_gil_state_release(state);
}

/**
 * pyg_closure_new:
 * callback: a Python callable object
 * extra_args: a tuple of extra arguments, or None/NULL.
 * swap_data: an alternative python object to pass first.
 *
 * Creates a GClosure wrapping a Python callable and optionally a set
 * of additional function arguments.  This is needed to attach python
 * handlers to signals, for instance.
 *
 * Returns: the new closure.
 */
GClosure *
pyg_closure_new(PyObject *callback, PyObject *extra_args, PyObject *swap_data)
{
    GClosure *closure;

    g_return_val_if_fail(callback != NULL, NULL);
    closure = g_closure_new_simple(sizeof(PyGClosure), NULL);
    g_closure_add_invalidate_notifier(closure, NULL, pyg_closure_invalidate);
    g_closure_set_marshal(closure, pyg_closure_marshal);
    Py_INCREF(callback);
    ((PyGClosure *)closure)->callback = callback;
    if (extra_args && extra_args != Py_None) {
	Py_INCREF(extra_args);
	if (!PyTuple_Check(extra_args)) {
	    PyObject *tmp = PyTuple_New(1);
	    PyTuple_SetItem(tmp, 0, extra_args);
	    extra_args = tmp;
	}
	((PyGClosure *)closure)->extra_args = extra_args;
    }
    if (swap_data) {
	Py_INCREF(swap_data);
	((PyGClosure *)closure)->swap_data = swap_data;
	closure->derivative_flag = TRUE;
    }
    return closure;
}

/**
 * pyg_closure_set_exception_handler:
 * @closure: a closure created with pyg_closure_new()
 * @handler: the handler to call when an exception occurs or NULL for none
 *
 * Sets the handler to call when an exception occurs during closure invocation.
 * The handler is responsible for providing a proper return value to the 
 * closure invocation. If @handler is %NULL, the default handler will be used.
 * The default handler prints the exception to stderr and doesn't touch the
 * closure's return value.
 */
void
pyg_closure_set_exception_handler(GClosure *closure,
				  PyClosureExceptionHandler handler)
{
    PyGClosure *pygclosure;
    
    g_return_if_fail(closure != NULL);

    pygclosure = (PyGClosure *)closure;
    pygclosure->exception_handler = handler;
}
/* -------------- PySignalClassClosure ----------------- */
/* a closure used for the `class closure' of a signal.  As this gets
 * all the info from the first argument to the closure and the
 * invocation hint, we can have a single closure that handles all
 * class closure cases.  We call a method by the name of the signal
 * with "do_" prepended.
 *
 *  We also remove the first argument from the * param list, as it is
 *  the instance object, which is passed * implicitly to the method
 *  object. */

static void
pyg_signal_class_closure_marshal(GClosure *closure,
				 GValue *return_value,
				 guint n_param_values,
				 const GValue *param_values,
				 gpointer invocation_hint,
				 gpointer marshal_data)
{
    PyGILState_STATE state;
    GObject *object;
    PyObject *object_wrapper;
    GSignalInvocationHint *hint = (GSignalInvocationHint *)invocation_hint;
    gchar *method_name, *tmp;
    PyObject *method;
    PyObject *params, *ret;
    guint i, len;

    state = pyg_gil_state_ensure();
    
    g_return_if_fail(invocation_hint != NULL);
    /* get the object passed as the first argument to the closure */
    object = g_value_get_object(&param_values[0]);
    g_return_if_fail(object != NULL && G_IS_OBJECT(object));

    /* get the wrapper for this object */
    object_wrapper = pygobject_new(object);
    g_return_if_fail(object_wrapper != NULL);

    /* construct method name for this class closure */
    method_name = g_strconcat("do_", g_signal_name(hint->signal_id), NULL);

    /* convert dashes to underscores.  For some reason, g_signal_name
     * seems to convert all the underscores in the signal name to
       dashes??? */
    for (tmp = method_name; *tmp != '\0'; tmp++)
	if (*tmp == '-') *tmp = '_';

    method = PyObject_GetAttrString(object_wrapper, method_name);
    g_free(method_name);

    if (!method) {
	PyErr_Clear();
	Py_DECREF(object_wrapper);
	pyg_gil_state_release(state);
	return;
    }
    Py_DECREF(object_wrapper);

    /* construct Python tuple for the parameter values; don't copy boxed values
       initially because we'll check after the call to see if a copy is needed. */
    params = PyTuple_New(n_param_values - 1);
    for (i = 1; i < n_param_values; i++) {
	PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);

	/* error condition */
	if (!item) {
	    Py_DECREF(params);
	    pyg_gil_state_release(state);
	    return;
	}
	PyTuple_SetItem(params, i - 1, item);
    }

    ret = PyObject_CallObject(method, params);
    
    /* Copy boxed values if others ref them, this needs to be done regardless of
       exception status. */
    len = PyTuple_Size(params);    
    for (i = 0; i < len; i++) {
	PyObject *item = PyTuple_GetItem(params, i);
	if (item != NULL && PyObject_TypeCheck(item, &PyGBoxed_Type)
	    && item->ob_refcnt != 1) {
	    PyGBoxed* boxed_item = (PyGBoxed*)item;
	    if (!boxed_item->free_on_dealloc) {
		boxed_item->boxed = g_boxed_copy(boxed_item->gtype, boxed_item->boxed);
		boxed_item->free_on_dealloc = TRUE;
	    }
	}
    }

    if (ret == NULL) {
	PyErr_Print();
	Py_DECREF(method);
	Py_DECREF(params);
	pyg_gil_state_release(state);
	return;
    }
    Py_DECREF(method);
    Py_DECREF(params);
    if (return_value)
	pyg_value_from_pyobject(return_value, ret);
    Py_DECREF(ret);
    pyg_gil_state_release(state);
}

/**
 * pyg_signal_class_closure_get:
 *
 * Returns the GClosure used for the class closure of signals.  When
 * called, it will invoke the method do_signalname (for the signal
 * "signalname").
 *
 * Returns: the closure.
 */
GClosure *
pyg_signal_class_closure_get(void)
{
    static GClosure *closure;

    if (closure == NULL) {
	closure = g_closure_new_simple(sizeof(GClosure), NULL);
	g_closure_set_marshal(closure, pyg_signal_class_closure_marshal);

	g_closure_ref(closure);
	g_closure_sink(closure);
    }
    return closure;
}

GClosure *
gclosure_from_pyfunc(PyGObject *object, PyObject *func)
{
    GSList *l;
    PyGObjectData *inst_data;
    inst_data = pyg_object_peek_inst_data(object->obj);
    if (inst_data)
    {
        for (l = inst_data->closures; l; l = l->next) {
            PyGClosure *pyclosure = l->data;
            int res;
            PyObject_Cmp(pyclosure->callback, func, &res);
            if (!res) {
                return (GClosure*)pyclosure;
            }
        }
    }
    return NULL;
}

/* ----- __doc__ descriptor for GObject and GInterface ----- */

static void
object_doc_dealloc(PyObject *self)
{
    PyObject_FREE(self);
}

/* append information about signals of a particular gtype */
static void
add_signal_docs(GType gtype, GString *string)
{
    GTypeClass *class = NULL;
    guint *signal_ids, n_ids = 0, i;

    if (G_TYPE_IS_CLASSED(gtype))
	class = g_type_class_ref(gtype);
    signal_ids = g_signal_list_ids(gtype, &n_ids);

    if (n_ids > 0) {
	g_string_append_printf(string, "Signals from %s:\n",
			       g_type_name(gtype));

	for (i = 0; i < n_ids; i++) {
	    GSignalQuery query;
	    guint j;

	    g_signal_query(signal_ids[i], &query);

	    g_string_append(string, "  ");
	    g_string_append(string, query.signal_name);
	    g_string_append(string, " (");
	    for (j = 0; j < query.n_params; j++) {
		g_string_append(string, g_type_name(query.param_types[j]));
		if (j != query.n_params - 1)
		    g_string_append(string, ", ");
	    }
	    g_string_append(string, ")");
	    if (query.return_type && query.return_type != G_TYPE_NONE) {
		g_string_append(string, " -> ");
		g_string_append(string, g_type_name(query.return_type));
	    }
	    g_string_append(string, "\n");
	}
	g_free(signal_ids);
	g_string_append(string, "\n");
    }
    if (class)
	g_type_class_unref(class);
}

static void
add_property_docs(GType gtype, GString *string)
{
    GObjectClass *class;
    GParamSpec **props;
    guint n_props = 0, i;
    gboolean has_prop = FALSE;
    G_CONST_RETURN gchar *blurb=NULL;

    class = g_type_class_ref(gtype);
    props = g_object_class_list_properties(class, &n_props);

    for (i = 0; i < n_props; i++) {
	if (props[i]->owner_type != gtype)
	    continue; /* these are from a parent type */

	/* print out the heading first */
	if (!has_prop) {
	    g_string_append_printf(string, "Properties from %s:\n",
				   g_type_name(gtype));
	    has_prop = TRUE;
	}
	g_string_append_printf(string, "  %s -> %s: %s\n",
			       g_param_spec_get_name(props[i]),
			       g_type_name(props[i]->value_type),
			       g_param_spec_get_nick(props[i]));

	/* g_string_append_printf crashes on win32 if the third 
	   argument is NULL. */
	blurb=g_param_spec_get_blurb(props[i]);
	if (blurb)
	    g_string_append_printf(string, "    %s\n",blurb);
    }
    g_free(props);
    if (has_prop)
	g_string_append(string, "\n");
    g_type_class_unref(class);
}

static PyObject *
object_doc_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
    GType gtype = 0;
    GString *string;
    PyObject *pystring;

    if (obj && pygobject_check(obj, &PyGObject_Type)) {
	gtype = G_OBJECT_TYPE(pygobject_get(obj));
	if (!gtype)
	    PyErr_SetString(PyExc_RuntimeError, "could not get object type");
    } else {
	gtype = pyg_type_from_object(type);
    }
    if (!gtype)
	return NULL;

    string = g_string_new_len(NULL, 512);

    if (g_type_is_a(gtype, G_TYPE_INTERFACE))
	g_string_append_printf(string, "Interface %s\n\n", g_type_name(gtype));
    else if (g_type_is_a(gtype, G_TYPE_OBJECT))
	g_string_append_printf(string, "Object %s\n\n", g_type_name(gtype));
    else
	g_string_append_printf(string, "%s\n\n", g_type_name(gtype));

    if (((PyTypeObject *) type)->tp_doc)
        g_string_append_printf(string, "%s\n\n", ((PyTypeObject *) type)->tp_doc);

    if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
	GType parent = G_TYPE_OBJECT;
        GArray *parents = g_array_new(FALSE, FALSE, sizeof(GType));
        int iparent;

        while (parent) {
            g_array_append_val(parents, parent);
            parent = g_type_next_base(gtype, parent);
        }
        
        for (iparent = parents->len - 1; iparent >= 0; --iparent) {
	    GType *interfaces;
	    guint n_interfaces, i;

            parent = g_array_index(parents, GType, iparent);
	    add_signal_docs(parent, string);
	    add_property_docs(parent, string);

	    /* add docs for implemented interfaces */
	    interfaces = g_type_interfaces(parent, &n_interfaces);
	    for (i = 0; i < n_interfaces; i++)
		add_signal_docs(interfaces[i], string);
	    g_free(interfaces);
	}
        g_array_free(parents, TRUE);
    }

    pystring = PyString_FromStringAndSize(string->str, string->len);
    g_string_free(string, TRUE);
    return pystring;
}

static PyTypeObject PyGObjectDoc_Type = {
    PyObject_HEAD_INIT(NULL)
    0,
    "gobject.GObject__doc__",
    sizeof(PyObject),
    0,
    (destructor)object_doc_dealloc,
    (printfunc)0,
    (getattrfunc)0,
    (setattrfunc)0,
    (cmpfunc)0,
    (reprfunc)0,
    0,
    0,
    0,
    (hashfunc)0,
    (ternaryfunc)0,
    (reprfunc)0,
    (getattrofunc)0,
    (setattrofunc)0,
    0,
    Py_TPFLAGS_DEFAULT,
    NULL,
    (traverseproc)0,
    (inquiry)0,
    (richcmpfunc)0,
    0,
    (getiterfunc)0,
    (iternextfunc)0,
    0,
    0,
    0,
    (PyTypeObject *)0,
    (PyObject *)0,
    (descrgetfunc)object_doc_descr_get,
    (descrsetfunc)0
};

/**
 * pyg_object_descr_doc_get:
 *
 * Returns an object intended to be the __doc__ attribute of GObject
 * wrappers.  When read in the context of the object it will return
 * some documentation about the signals and properties of the object.
 *
 * Returns: the descriptor.
 */
PyObject *
pyg_object_descr_doc_get(void)
{
    static PyObject *doc_descr = NULL;

    if (!doc_descr) {
	PyGObjectDoc_Type.ob_type = &PyType_Type;
	if (PyType_Ready(&PyGObjectDoc_Type))
	    return NULL;

	doc_descr = PyObject_NEW(PyObject, &PyGObjectDoc_Type);
	if (doc_descr == NULL)
	    return NULL;
    }
    return doc_descr;
}


/**
 * pyg_pyobj_to_unichar_conv:
 *
 * Converts PyObject value to a unichar and write result to memory
 * pointed to by ptr.  Follows the calling convention of a ParseArgs
 * converter (O& format specifier) so it may be used to convert function
 * arguments.
 *
 * Returns: 1 if the conversion succeeds and 0 otherwise.  If the conversion
 *          did not succeesd, a Python exception is raised
 */
int pyg_pyobj_to_unichar_conv(PyObject* py_obj, void* ptr)
{
    gunichar* u = ptr;
    const Py_UNICODE* uni_buffer;
    PyObject* tmp_uni = NULL;
    
    if (PyUnicode_Check(py_obj)) {
	tmp_uni = py_obj;
	Py_INCREF(tmp_uni);
    }
    else {
	tmp_uni = PyUnicode_FromObject(py_obj);
	if (tmp_uni == NULL)
	    goto failure;
    }
    
    if ( PyUnicode_GetSize(tmp_uni) != 1) {
	PyErr_SetString(PyExc_ValueError, "unicode character value must be 1 character uniode string");
	goto failure;
    }
    uni_buffer = PyUnicode_AsUnicode(tmp_uni);
    if ( uni_buffer == NULL)
	goto failure;
    *u = uni_buffer[0];
    
    Py_DECREF(tmp_uni);
    return 1;
    
  failure:
    Py_XDECREF(tmp_uni);
    return 0;
}


int 
pyg_param_gvalue_from_pyobject(GValue* value, 
                               PyObject* py_obj, 
			       const GParamSpec* pspec)
{
    if (G_IS_PARAM_SPEC_UNICHAR(pspec)) {
	gunichar u;
	
	if (!pyg_pyobj_to_unichar_conv(py_obj, &u)) {
	    PyErr_Clear();
	    return -1;
	}
        g_value_set_uint(value, u);
	return 0;
    }
    else if (G_IS_PARAM_SPEC_VALUE_ARRAY(pspec))
	return pyg_value_array_from_pyobject(value, py_obj,
					     G_PARAM_SPEC_VALUE_ARRAY(pspec));
    else {
	return pyg_value_from_pyobject(value, py_obj);
    }
}

PyObject* 
pyg_param_gvalue_as_pyobject(const GValue* gvalue,
                             gboolean copy_boxed, 
			     const GParamSpec* pspec)
{
    if (G_IS_PARAM_SPEC_UNICHAR(pspec)) {
	gunichar u;
	Py_UNICODE uni_buffer[2] = { 0, 0 };
	
	u = g_value_get_uint(gvalue);
	uni_buffer[0] = u;
	return PyUnicode_FromUnicode(uni_buffer, 1);
    }
    else {
	return pyg_value_as_pyobject(gvalue, copy_boxed);
    }
}

/**
 * pyg_type_registration_callback
 * @gtypename: type name
 * @callback: function to run
 *
 */
typedef struct {
    PyGTypeRegistrationFunction callback;
    gpointer data;
} CustomTypeData;

void 
pyg_type_register_custom_callback(const gchar *typename,
				  PyGTypeRegistrationFunction callback,
				  gpointer user_data)
{
    CustomTypeData *data;

    if (!custom_type_registration)
	custom_type_registration = g_hash_table_new_full (g_str_hash, g_str_equal,
							  g_free, g_free);

    data = g_new (CustomTypeData, 1);
    data->callback = callback;
    data->data = user_data;

    g_hash_table_insert(custom_type_registration,
			g_strdup(typename),
			data);
}

PyTypeObject *
pyg_type_get_custom(const gchar *name)
{
    CustomTypeData *data;
    PyTypeObject *retval;
    
    if (!custom_type_registration)
	return NULL;

    data = g_hash_table_lookup(custom_type_registration, name);
    if (!data)
	return NULL;

    retval = data->callback(name, data->data);

    g_hash_table_remove(custom_type_registration, name);

    return retval;
}

GType
_pyg_type_from_name(const gchar *name)
{
    GType type;

    type = g_type_from_name(name);
    if (type == G_TYPE_INVALID) {
	pyg_type_get_custom(name);
	type = g_type_from_name(name);
    }
    
    return type;
}



syntax highlighted by Code2HTML, v. 0.9.1