/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <tqstring.h>
#include <tqregexp.h>
#include <tqapplication.h>
#include <tqcanvas.h>
#include <tqlistview.h>
#include <tqiconview.h>
#include <tqtable.h>
#include <tqpopupmenu.h>
#include <tqlayout.h>
#include <tqmetaobject.h>
#include <tqvaluelist.h>
#include <tqobjectlist.h>
#include <tqtextcodec.h>
#include <tqhostaddress.h>
#include <tqpair.h>

#include <private/qucomextra_p.h>

#include "smoke.h"

#undef DEBUG
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_POSIX
#define __USE_POSIX
#endif
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#include <ruby.h>

#include "marshall.h"
#include "qtruby.h"
#include "smokeruby.h"

#ifndef HINT_BYTES
#define HINT_BYTES HINT_BYTE
#endif

#define HAVE_STRLCAT_PROTO 1
#define HAVE_STRLCPY_PROTO 1
#include "config.h"

#ifndef HAVE_RUBY_1_9
#define RARRAY_LEN(x) (RARRAY(x)->len)
#define RSTRING_LEN(x) (RSTRING(x)->len)
#define rb_str_catf_1 rb_str_catf
#endif

extern "C" {
extern VALUE set_obj_info(const char * className, smokeruby_object * o);
extern VALUE qt_internal_module;
extern VALUE qvariant_class;
extern bool application_terminated;
};

extern bool isDerivedFromByName(Smoke *smoke, const char *className, const char *baseClassName);
extern void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr);

static const char * (*_kde_resolve_classname)(Smoke*, int, void*) = 0;

extern "C" {

void
set_kde_resolve_classname(const char * (*kde_resolve_classname) (Smoke*, int, void *))
{
	_kde_resolve_classname = kde_resolve_classname;
}

};

void
mark_qobject_children(TQObject * qobject)
{
	VALUE obj;
	
	const TQObjectList *l = qobject->children();
	if (l == 0) {
		return;
	}
	TQObjectListIt it( *l ); // iterate over the children
	TQObject *child;

	while ( (child = it.current()) != 0 ) {
		++it;
		obj = getPointerObject(child);
		if (obj != Qnil) {
			if(do_debug & qtdb_gc) tqWarning("Marking (%s*)%p -> %p\n", child->className(), child, (void*)obj);
			rb_gc_mark(obj);
		}
		
		mark_qobject_children(child);
	}
}

void
smokeruby_mark(void * p)
{
	VALUE obj;
    smokeruby_object * o = (smokeruby_object *) p;
    const char *className = o->smoke->classes[o->classId].className;
	
	if(do_debug & qtdb_gc) tqWarning("Checking for mark (%s*)%p\n", className, o->ptr);
		
    if(o->ptr && o->allocated) {
		if (isDerivedFromByName(o->smoke, className, "TQListView")) {
			TQListView * listview = (TQListView *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQListView"));
			TQListViewItemIterator it(listview);
			TQListViewItem *item;

			while ( (item = it.current()) != 0 ) {
				++it;
				obj = getPointerObject(item);
				if (obj != Qnil) {
					if(do_debug & qtdb_gc) tqWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj);
					rb_gc_mark(obj);
				}
			}
			return;
		}
		
		if (isDerivedFromByName(o->smoke, className, "TQTable")) {
			TQTable * table = (TQTable *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQTable"));
			TQTableItem *item;

			for ( int row = 0; row < table->numRows(); row++ ) {
				for ( int col = 0; col < table->numCols(); col++ ) {
					item = table->item(row, col);
					obj = getPointerObject(item);
					if (obj != Qnil) {
						if(do_debug & qtdb_gc) tqWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj);
						rb_gc_mark(obj);
					}
				}
			}
			return;		
		}
		
		if (isDerivedFromByName(o->smoke, className, "TQCanvas")) {
			TQCanvas * canvas = (TQCanvas *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQCanvas"));
    		TQCanvasItemList list = canvas->allItems();
    		for ( TQCanvasItemList::iterator it = list.begin(); it != list.end(); ++it ) {
				obj = getPointerObject(*it);
				if (obj != Qnil) {
					if(do_debug & qtdb_gc) tqWarning("Marking (%s*)%p -> %p\n", className, *it, (void*)obj);
					rb_gc_mark(obj);
				}
			}
			return;
		}

		if (isDerivedFromByName(o->smoke, className, "TQCanvasItem")) {
			TQCanvasItem * item = (TQCanvasItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQCanvasItem"));
			TQCanvas * canvas = item->canvas();
			obj = getPointerObject(canvas);
			if (obj != Qnil) {
				if(do_debug & qtdb_gc) tqWarning("Marking (%s*)%p -> %p\n", "TQCanvas", canvas, (void*)obj);
				rb_gc_mark(obj);
			}
			return;
		}
		
		if (isDerivedFromByName(o->smoke, className, "TQObject")) {
			TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject"));
			mark_qobject_children(qobject);
			return;
		}
	}
}

void
smokeruby_free(void * p)
{
    smokeruby_object *o = (smokeruby_object*)p;
    const char *className = o->smoke->classes[o->classId].className;
	
	if(do_debug & qtdb_gc) tqWarning("Checking for delete (%s*)%p allocated: %s\n", className, o->ptr, o->allocated ? "true" : "false");
    
	if(application_terminated || !o->allocated || o->ptr == 0) {
		free(o);
		return;
	}
	
	unmapPointer(o, o->classId, 0);
	object_count --;
	
	if (	tqstrcmp(className, "TQObject") == 0
			|| tqstrcmp(className, "TQListBoxItem") == 0
			|| tqstrcmp(className, "TQStyleSheetItem") == 0
			|| tqstrcmp(className, "KCommand") == 0
			|| tqstrcmp(className, "KNamedCommand") == 0
			|| tqstrcmp(className, "KMacroCommand") == 0
			|| tqstrcmp(className, "TDEAboutData") == 0
			|| tqstrcmp(className, "TDECmdLineArgs") == 0
			|| tqstrcmp(className, "TQSqlCursor") == 0 )
	{
		// Don't delete instances of these classes for now
		free(o);
		return;
	} else if (isDerivedFromByName(o->smoke, className, "TQLayoutItem")) {
		TQLayoutItem * item = (TQLayoutItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQLayoutItem"));
		if (item->layout() != 0 || item->widget() != 0 || item->spacerItem() != 0) {
			free(o);
			return;
		}
	} else if (tqstrcmp(className, "TQIconViewItem") == 0) {
		TQIconViewItem * item = (TQIconViewItem *) o->ptr;
		if (item->iconView() != 0) {
			free(o);
			return;
		}
	} else if (tqstrcmp(className, "TQCheckListItem") == 0) {
		TQCheckListItem * item = (TQCheckListItem *) o->ptr;
		if (item->parent() != 0 || item->listView() != 0) {
			free(o);
			return;
		}
	} else if (tqstrcmp(className, "TQListViewItem") == 0) {
		TQListViewItem * item = (TQListViewItem *) o->ptr;
		if (item->parent() != 0 || item->listView() != 0) {
			free(o);
			return;
		}
	} else if (isDerivedFromByName(o->smoke, className, "TQTableItem")) {
		TQTableItem * item = (TQTableItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQTableItem"));
		if (item->table() != 0) {
			free(o);
			return;
		}
	} else if (tqstrcmp(className, "TQPopupMenu") == 0) {
		TQPopupMenu * item = (TQPopupMenu *) o->ptr;
		if (item->parentWidget(false) != 0) {
			free(o);
			return;
		}
	} else if (isDerivedFromByName(o->smoke, className, "TQWidget")) {
		TQWidget * qwidget = (TQWidget *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQWidget"));
		if (qwidget->parentWidget(true) != 0) {
			free(o);
			return;
		}
	} else if (isDerivedFromByName(o->smoke, className, "TQObject")) {
		TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject"));
		if (qobject->parent() != 0) {
			free(o);
			return;
		}
	}
			
	if(do_debug & qtdb_gc) tqWarning("Deleting (%s*)%p\n", className, o->ptr);
	
	char *methodName = new char[strlen(className) + 2];
	methodName[0] = '~';
	strcpy(methodName + 1, className);
	Smoke::Index nameId = o->smoke->idMethodName(methodName);
	Smoke::Index meth = o->smoke->findMethod(o->classId, nameId);
	if(meth > 0) {
		Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method];
		Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn;
		Smoke::StackItem i[1];
		(*fn)(m.method, o->ptr, i);
	}
	delete[] methodName;
	free(o);
	
    return;
}

/*
 * Given an approximate classname and a qt instance, try to improve the resolution of the name
 * by using the various Qt rtti mechanisms for TQObjects, TQEvents and TQCanvasItems
 */
static const char *
resolve_classname(Smoke* smoke, int classId, void * ptr)
{
	if (isDerivedFromByName(smoke, smoke->classes[classId].className, "TQEvent")) {
		TQEvent * qevent = (TQEvent *) smoke->cast(ptr, classId, smoke->idClass("TQEvent"));
		switch (qevent->type()) {
		case TQEvent::ChildInserted:
		case TQEvent::ChildRemoved:
			return "TQt::ChildEvent";
		case TQEvent::Close:
			return "TQt::CloseEvent";
		case TQEvent::ContextMenu:
			return "TQt::ContextMenuEvent";
//		case TQEvent::User:
//			return "TQt::CustomEvent";
		case TQEvent::DragEnter:
			return "TQt::DragEnterEvent";
		case TQEvent::DragLeave:
			return "TQt::DragLeaveEvent";
		case TQEvent::DragMove:
			return "TQt::DragMoveEvent";
		case TQEvent::DragResponse:
			return "TQt::DragResponseEvent";
		case TQEvent::Drop:
			return "TQt::DropEvent";
		case TQEvent::FocusIn:
		case TQEvent::FocusOut:
			return "TQt::FocusEvent";
		case TQEvent::Hide:
			return "TQt::HideEvent";
		case TQEvent::KeyPress:
		case TQEvent::KeyRelease:
			return "TQt::KeyEvent";
		case TQEvent::IMStart:
		case TQEvent::IMCompose:
		case TQEvent::IMEnd:
			return "TQt::IMEvent";
		case TQEvent::MouseButtonPress:
		case TQEvent::MouseButtonRelease:
		case TQEvent::MouseButtonDblClick:
		case TQEvent::MouseMove:
			return "TQt::MouseEvent";
		case TQEvent::Move:
			return "TQt::MoveEvent";
		case TQEvent::Paint:
			return "TQt::PaintEvent";
		case TQEvent::Resize:
			return "TQt::ResizeEvent";
		case TQEvent::Show:
			return "TQt::ShowEvent";
	//	case TQEvent::Tablet:
	//		 return "TQt::TabletEvent";
		case TQEvent::Timer:
			return "TQt::TimerEvent";
		case TQEvent::Wheel:
			return "TQt::WheelEvent";
		default:
			break;
		}
	} else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "TQObject")) {
		TQObject * qobject = (TQObject *) smoke->cast(ptr, classId, smoke->idClass("TQObject"));
		TQMetaObject * meta = qobject->metaObject();

		while (meta != 0) {
			Smoke::Index classId = smoke->idClass(meta->className());
			if (classId != 0) {
				return smoke->binding->className(classId);
			}

			meta = meta->superClass();
		}
	} else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "TQCanvasItem")) {
		TQCanvasItem * qcanvasitem = (TQCanvasItem *) smoke->cast(ptr, classId, smoke->idClass("TQCanvasItem"));
		switch (qcanvasitem->rtti()) {
		case TQCanvasItem::Rtti_Sprite:
			return "TQt::CanvasSprite";
		case TQCanvasItem::Rtti_PolygonalItem:
			return "TQt::CanvasPolygonalItem";
		case TQCanvasItem::Rtti_Text:
			return "TQt::CanvasText";
		case TQCanvasItem::Rtti_Polygon:
			return "TQt::CanvasPolygon";
		case TQCanvasItem::Rtti_Rectangle:
			return "TQt::CanvasRectangle";
		case TQCanvasItem::Rtti_Ellipse:
			return "TQt::CanvasEllipse";
		case TQCanvasItem::Rtti_Line:
			return "TQt::CanvasLine";
		case TQCanvasItem::Rtti_Spline:
			return "TQt::CanvasSpline";
		default:
			break;
		}
	} else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "TQListViewItem")) {
		TQListViewItem * item = (TQListViewItem *) smoke->cast(ptr, classId, smoke->idClass("TQListViewItem"));
		switch (item->rtti()) {
		case 0:
			return "TQt::ListViewItem";
		case 1:
			return "TQt::CheckListItem";
		default:
			return "TQt::ListViewItem";
			break;
		}
	} else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "TQTableItem")) {
		TQTableItem * item = (TQTableItem *) smoke->cast(ptr, classId, smoke->idClass("TQTableItem"));
		switch (item->rtti()) {
		case 0:
			return "TQt::TableItem";
		case 1:
			return "TQt::ComboTableItem";
		case 2:
			return "TQt::CheckTableItem";
		default:
			return "TQt::TableItem";
			break;
		}
	}
	
	if (_kde_resolve_classname != 0) {
		return (*_kde_resolve_classname)(smoke, classId, ptr);
	}
	
	return smoke->binding->className(classId);
}

bool
matches_arg(Smoke *smoke, Smoke::Index meth, Smoke::Index argidx, const char *argtype)
{
    Smoke::Index *arg = smoke->argumentList + smoke->methods[meth].args + argidx;
    SmokeType type = SmokeType(smoke, *arg);
    return type.name() && tqstrcmp(type.name(), argtype) == 0;
}

void *
construct_copy(smokeruby_object *o)
{
    const char *className = o->smoke->className(o->classId);
    int classNameLen = strlen(className);
    char *ccSig = new char[classNameLen + 2];       // copy constructor signature
    strcpy(ccSig, className);
    strcat(ccSig, "#");
    Smoke::Index ccId = o->smoke->idMethodName(ccSig);
    delete[] ccSig;

    char *ccArg = new char[classNameLen + 8];
    sprintf(ccArg, "const %s&", className);

    Smoke::Index ccMeth = o->smoke->findMethod(o->classId, ccId);

    if(!ccMeth) {
	delete[] ccArg;
	return 0;
    }
	Smoke::Index method = o->smoke->methodMaps[ccMeth].method;
    if(method > 0) {
	// Make sure it's a copy constructor
	if(!matches_arg(o->smoke, method, 0, ccArg)) {
            delete[] ccArg;
	    return 0;
        }
        delete[] ccArg;
        ccMeth = method;
    } else {
        // ambiguous method, pick the copy constructor
	Smoke::Index i = -method;
	while(o->smoke->ambiguousMethodList[i]) {
	    if(matches_arg(o->smoke, o->smoke->ambiguousMethodList[i], 0, ccArg))
		break;
            i++;
	}
        delete[] ccArg;
	ccMeth = o->smoke->ambiguousMethodList[i];
	if(!ccMeth)
	    return 0;
    }

    // Okay, ccMeth is the copy constructor. Time to call it.
    Smoke::StackItem args[2];
    args[0].s_voidp = 0;
    args[1].s_voidp = o->ptr;
    Smoke::ClassFn fn = o->smoke->classes[o->classId].classFn;
    (*fn)(o->smoke->methods[ccMeth].method, 0, args);
    return args[0].s_voidp;
}

void
marshall_basetype(Marshall *m)
{
    switch(m->type().elem()) {
      case Smoke::t_bool:
	switch(m->action()) {
	  case Marshall::FromVALUE:
		if (TYPE(*(m->var())) == T_OBJECT) {
			// A TQt::Boolean has been passed as a value
			VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, *(m->var()));
	    	m->item().s_bool = (temp == Qtrue ? true : false);
		} else {
	    	m->item().s_bool = (*(m->var()) == Qtrue ? true : false);
		}
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = m->item().s_bool ? Qtrue : Qfalse;
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_char:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_char = NUM2CHR(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = CHR2FIX(m->item().s_char);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_uchar:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_uchar = NUM2CHR(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = CHR2FIX(m->item().s_uchar);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_short:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_short = (short) NUM2INT(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = INT2NUM(m->item().s_short);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_ushort:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_ushort = (unsigned short) NUM2UINT(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = UINT2NUM(m->item().s_ushort);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_int:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    if (TYPE(*(m->var())) == T_OBJECT) {
			m->item().s_int = (int) NUM2INT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())));
		} else {
	    	m->item().s_int = (int) NUM2INT(*(m->var()));
		}
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = INT2NUM(m->item().s_int);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_uint:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    if (TYPE(*(m->var())) == T_OBJECT) {
			m->item().s_int = (unsigned int) NUM2UINT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())));
		} else {
	    	m->item().s_uint = (unsigned int) NUM2UINT(*(m->var()));
		}
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = UINT2NUM(m->item().s_uint);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_long:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    if (TYPE(*(m->var())) == T_OBJECT) {
			m->item().s_int = (long) NUM2LONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())));
		} else {
	    	m->item().s_long = (long) NUM2LONG(*(m->var()));
		}
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = INT2NUM(m->item().s_long);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_ulong:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    if (TYPE(*(m->var())) == T_OBJECT) {
			m->item().s_int = (unsigned long) NUM2ULONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())));
		} else {
	    	m->item().s_ulong = (unsigned long) NUM2ULONG(*(m->var()));
		}
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = INT2NUM(m->item().s_ulong);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_float:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_float = (float) NUM2DBL(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = rb_float_new((double) m->item().s_float);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_double:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    m->item().s_double = (double) NUM2DBL(*(m->var()));
	    break;
	  case Marshall::ToVALUE:
	    *(m->var()) = rb_float_new(m->item().s_double);
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_enum:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	  	{
		if (TYPE(*(m->var())) == T_OBJECT) {
			// A TQt::Enum is a subclass of TQt::Integer, so 'get_qinteger()' can be called ok
			VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()));
	    	m->item().s_enum = (long) NUM2LONG(temp);
		} else {
	    	m->item().s_enum = (long) NUM2LONG(*(m->var()));
		}
		}
	    break;
	  case Marshall::ToVALUE:
		*(m->var()) = rb_funcall(	qt_internal_module, 
									rb_intern("create_qenum"), 
									2, INT2NUM(m->item().s_enum), rb_str_new2(m->type().name()) );
 
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      case Smoke::t_class:
	switch(m->action()) {
	  case Marshall::FromVALUE:
	    {
		if(*(m->var()) == Qnil) {
            m->item().s_class = 0;
		    break;
		}
		if(TYPE(*(m->var())) != T_DATA) {
            rb_raise(rb_eArgError, "Invalid type, expecting %s\n", m->type().name());
		    break;
		}
		
		smokeruby_object *o = value_obj_info(*(m->var()));
		if(!o || !o->ptr) {
                    if(m->type().isRef()) {
                        rb_warning("References can't be nil\n");
                        m->unsupported();
                    }
		    m->item().s_class = 0;
		    break;
		}
		void *ptr = o->ptr;
		if(!m->cleanup() && m->type().isStack()) {
		    ptr = construct_copy(o);
		}
		const Smoke::Class &c = m->smoke()->classes[m->type().classId()];
		ptr = o->smoke->cast(
		    ptr,				// pointer
		    o->classId,				// from
		    o->smoke->idClass(c.className)	// to
		);
		m->item().s_class = ptr;
		break;
	    }
	    break;
	  case Marshall::ToVALUE:
	    {
		if(m->item().s_voidp == 0) {
                    *(m->var()) = Qnil;
		    break;
		}

		void *p = m->item().s_voidp;
		VALUE obj = getPointerObject(p);
		if(obj != Qnil) {
                    *(m->var()) = obj;
		    break;
		}

		smokeruby_object  * o = (smokeruby_object *) malloc(sizeof(smokeruby_object));
		o->smoke = m->smoke();
		o->classId = m->type().classId();
		o->ptr = p;
		o->allocated = false;

		const char * classname = resolve_classname(o->smoke, o->classId, o->ptr);
		
		if(m->type().isConst() && m->type().isRef()) {
		    p = construct_copy( o );
		    if(p) {
			o->ptr = p;
			o->allocated = true;
		    }
		}
		
		obj = set_obj_info(classname, o);
		if (do_debug & qtdb_calls) {
			tqWarning("allocating %s %p -> %p\n", classname, o->ptr, (void*)obj);
		}

		if(m->type().isStack()) {
		    o->allocated = true;
			// Keep a mapping of the pointer so that it is only wrapped once as a ruby VALUE
		    mapPointer(obj, o, o->classId, 0);
		}
			
		*(m->var()) = obj;
	    }
	    break;
	  default:
	    m->unsupported();
	    break;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_void(Marshall * /*m*/) {}
static void marshall_unknown(Marshall *m) {
    m->unsupported();
}

static void marshall_charP(Marshall *m) {
    switch(m->action()) {
    case Marshall::FromVALUE:
    {
        VALUE rv = *(m->var());
	    if (rv == Qnil) {
            m->item().s_voidp = 0;
            break;
        }
	
        int len = RSTRING_LEN(rv);
        char* mem = (char*) malloc(len+1);
        memcpy(mem, StringValuePtr(rv), len);
        mem[len] ='\0';
        m->item().s_voidp = mem;
    }
        break;
      case Marshall::ToVALUE:
	{
	    char *p = (char*)m->item().s_voidp;
	    if(p)
                *(m->var()) = rb_str_new2(p);
	    else
                *(m->var()) = Qnil;
	    if(m->cleanup())
		delete[] p;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_ucharP(Marshall *m) {
    switch(m->action()) {
    case Marshall::FromVALUE:
    {
        VALUE rv = *(m->var());
        if (rv == Qnil) {
            m->item().s_voidp = 0;
            break;
        }
        int len = RSTRING_LEN(rv);
        char* mem = (char*) malloc(len+1);
        memcpy(mem, StringValuePtr(rv), len);
        mem[len] ='\0';
        m->item().s_voidp = mem;
	}
	break;
      case Marshall::ToVALUE:
      default:
	m->unsupported();
	break;
    }
}

static const char * KCODE = 0;
static TQTextCodec *codec = 0;

static void 
init_codec() {
	VALUE temp = rb_gv_get("$KCODE");
	KCODE = StringValuePtr(temp);
	if (tqstrcmp(KCODE, "EUC") == 0) {
		codec = TQTextCodec::codecForName("eucJP");
	} else if (tqstrcmp(KCODE, "SJIS") == 0) {
		codec = TQTextCodec::codecForName("Shift-JIS");
	}
}

TQString* 
qstringFromRString(VALUE rstring) {
	if (KCODE == 0) {
		init_codec();
	}
	
	TQString *	s;
	if (tqstrcmp(KCODE, "UTF8") == 0)
		s = new TQString(TQString::fromUtf8(StringValuePtr(rstring), RSTRING_LEN(rstring)));
	else if (tqstrcmp(KCODE, "EUC") == 0)
		s = new TQString(codec->toUnicode(StringValuePtr(rstring)));
	else if (tqstrcmp(KCODE, "SJIS") == 0)
		s = new TQString(codec->toUnicode(StringValuePtr(rstring)));
	else if(tqstrcmp(KCODE, "NONE") == 0)
		s = new TQString(TQString::fromLatin1(StringValuePtr(rstring)));
	else
		s = new TQString(TQString::fromLocal8Bit(StringValuePtr(rstring), RSTRING_LEN(rstring)));
	return s;
}

VALUE 
rstringFromTQString(TQString * s) {
	if (KCODE == 0) {
		init_codec();
	}
	
	if (tqstrcmp(KCODE, "UTF8") == 0)
		return rb_str_new2(s->utf8());
	else if (tqstrcmp(KCODE, "EUC") == 0)
		return rb_str_new2(codec->fromUnicode(*s));
	else if (tqstrcmp(KCODE, "SJIS") == 0)
		return rb_str_new2(codec->fromUnicode(*s));
	else if (tqstrcmp(KCODE, "NONE") == 0)
		return rb_str_new2(s->latin1());
	else
		return rb_str_new2(s->local8Bit());
}

static void marshall_TQString(Marshall *m) {
	switch(m->action()) {
	case Marshall::FromVALUE:
	{
		TQString* s = 0;
		if ( *(m->var()) != Qnil) {
			s = qstringFromRString(*(m->var()));
		} else {
			s = new TQString(TQString::null);
		}
		
		m->item().s_voidp = s;
		m->next();
		
		if (!m->type().isConst() && *(m->var()) != Qnil && s != 0 && !s->isNull()) {
			rb_str_resize(*(m->var()), 0);
			VALUE temp = rstringFromTQString(s);
			rb_str_cat2(*(m->var()), StringValuePtr(temp));
		}
	    
		if (s != 0 && m->cleanup()) {
			delete s;
		}
	}
	break;
	case Marshall::ToVALUE:
	{
		TQString *s = (TQString*)m->item().s_voidp;
		if (s != 0) {
			if (s->isNull()) {
				*(m->var()) = Qnil;
			} else {
				*(m->var()) = rstringFromTQString(s);
			}
			if (m->cleanup() || m->type().isStack()) {
				delete s;
			}
		} else {
			*(m->var()) = Qnil;
		}
	}
	break;
	default:
		m->unsupported();
		break;
    }
}

// The only way to convert a TQChar to a TQString is to
// pass a TQChar to a TQString constructor. However,
// TQStrings aren't in the QtRuby api, so add this
// convenience method 'TQt::Char.to_s' to get a ruby
// string from a TQt::Char.
VALUE
qchar_to_s(VALUE self)
{
	smokeruby_object *o = value_obj_info(self);
	if (o == 0 || o->ptr == 0) {
		return Qnil;
	}

	TQChar * qchar = (TQChar*) o->ptr;
	TQString s(*qchar);
	return rstringFromTQString(&s);
}

#if 0
static const char *not_ascii(const char *s, uint &len)
{
    bool r = false;
    for(; *s ; s++, len--)
      if((uint)*s > 0x7F)
      {
        r = true;
        break;
      }
    return r ? s : 0L;
}
#endif

static void marshall_TQCString(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    TQCString *s = 0;
	    VALUE rv = *(m->var());
	    if (rv == Qnil) {
		s = new TQCString(); 
        } else {
		// Add 1 to the ruby string length to allow for a TQCString '\0' terminator
		s = new TQCString(StringValuePtr(*(m->var())), RSTRING_LEN(*(m->var())) + 1); 
		}
	    m->item().s_voidp = s;
	    
		m->next();
	    
		if (!m->type().isConst() && rv != Qnil && s != 0) {
			rb_str_resize(rv, 0);
			rb_str_cat2(rv, (const char *)*s);
		}
	    if(s && m->cleanup())
		delete s;
	}
	break;
      case Marshall::ToVALUE:
	{
	    TQCString *s = (TQCString*)m->item().s_voidp;
	    if(s && (const char *) *s != 0) {
		*(m->var()) = rb_str_new2((const char *)*s);
//                const char * p = (const char *)*s;
//                uint len =  s->length();
//                if(not_ascii(p,len))
//                {
//                  #if PERL_VERSION == 6 && PERL_SUBVERSION == 0
//                  TQTextCodec* c = TQTextCodec::codecForMib(106); // utf8
//                  if(c->heuristicContentMatch(p,len) >= 0)
//                  #else
//                  if(is_utf8_string((U8 *)p,len))
//                  #endif
//                    SvUTF8_on(*(m->var()));
//                }
	    } else {
			if (m->type().isConst()) {
                *(m->var()) = Qnil;
			} else {
                *(m->var()) = rb_str_new2("");
			}
	    }
		m->next();

		if (!m->type().isConst() && s != 0) {
			*s = (const char *) StringValuePtr(*(m->var()));
		}
	    
	    if(s && m->cleanup())
		delete s;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_TQCOORD_array(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE av = *(m->var());
	    if (TYPE(av) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }
	    int count = RARRAY_LEN(av);
	    TQCOORD *coord = new TQCOORD[count + 2];
	    for(long i = 0; i < count; i++) {
		VALUE svp = rb_ary_entry(av, i);
		coord[i] = NUM2INT(svp);
	    }
	    m->item().s_voidp = coord;
	    m->next();
	}
	break;
      default:
	m->unsupported();
    }
}

static void marshall_longlong(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    m->item().s_voidp = new long long;
	    *(long long *)m->item().s_voidp = rb_num2ll(*(m->var()));
		
	    m->next();
		
	    if(m->cleanup() && m->type().isConst()) {
			delete (long long *) m->item().s_voidp;
		}
	}
	break;
      case Marshall::ToVALUE:
	{
	    *(m->var()) = rb_ll2inum(*(long long *) m->item().s_voidp);
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_ulonglong(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    m->item().s_voidp = new unsigned long long;
	    *(long long *)m->item().s_voidp = rb_num2ull(*(m->var()));
		
	    m->next();
		
	    if(m->cleanup() && m->type().isConst()) {
			delete (unsigned long long *) m->item().s_voidp;
		}
	}
	break;
      case Marshall::ToVALUE:
	{
	    *(m->var()) = rb_ull2inum(*(unsigned long long *) m->item().s_voidp);
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_intR(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE rv = *(m->var());
		int * i = new int;
		if (TYPE(rv) == T_OBJECT) {
			// A TQt::Integer has been passed as an integer value
			VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, rv);
			*i = NUM2INT(temp);
			m->item().s_voidp = i;
			m->next();
			rb_funcall(qt_internal_module, rb_intern("set_qinteger"), 2, rv, INT2NUM(*i));
			rv = temp;
		} else {
			*i = NUM2INT(rv);
			m->item().s_voidp = i;
			m->next();
		}
	    if(m->cleanup() && m->type().isConst()) {
			delete i;
	    } else {
		m->item().s_voidp = new int((int)NUM2INT(rv));
	    }
	}
	break;
      case Marshall::ToVALUE:
	{
	    int *ip = (int*)m->item().s_voidp;
	    VALUE rv = *(m->var());
	    if(!ip) {
	        rv = Qnil;
		break;
	    }
	    *(m->var()) = INT2NUM(*ip);
	    m->next();
	    if(!m->type().isConst())
		*ip = NUM2INT(*(m->var()));
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_boolR(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE rv = *(m->var());
		bool * b = new bool;
		if (TYPE(rv) == T_OBJECT) {
			// A TQt::Boolean has been passed as a value
			VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, rv);
			*b = (temp == Qtrue ? true : false);
			m->item().s_voidp = b;
			m->next();
			rb_funcall(qt_internal_module, rb_intern("set_qboolean"), 2, rv, (*b ? Qtrue : Qfalse));
		} else {
			*b = (rv == Qtrue ? true : false);
			m->item().s_voidp = b;
			m->next();
		}
	    	if(m->cleanup() && m->type().isConst()) {
			delete b;
		}
	}
	break;
      case Marshall::ToVALUE:
	{
	    bool *ip = (bool*)m->item().s_voidp;
	    if(!ip) {
	    *(m->var()) = Qnil;
		break;
	    }
	    *(m->var()) = (*ip?Qtrue:Qfalse);
	    m->next();
	    if(!m->type().isConst())
		*ip = *(m->var()) == Qtrue ? true : false;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

static void marshall_charP_array(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE arglist = *(m->var());
	    if (arglist == Qnil
	    || TYPE(arglist) != T_ARRAY
	    || RARRAY_LEN(arglist) == 0 )
	    {
                m->item().s_voidp = 0;
                break;
	    }

	    char **argv = new char *[RARRAY_LEN(arglist) + 1];
	    long i;
	    for(i = 0; i < RARRAY_LEN(arglist); i++) {
                VALUE item = rb_ary_entry(arglist, i);
                char *s = StringValuePtr(item);
                argv[i] = new char[strlen(s) + 1];
                strcpy(argv[i], s);
	    }
	    argv[i] = 0;
	    m->item().s_voidp = argv;
	    m->next();
	    if(m->cleanup()) {
		rb_ary_clear(arglist);
		for(i = 0; argv[i]; i++)
		    rb_ary_push(arglist, rb_str_new2(argv[i]));
	    }
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQStringList(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE: 
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }

	    int count = RARRAY_LEN(list);
	    TQStringList *stringlist = new TQStringList;

	    for(long i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
		if(TYPE(item) != T_STRING) {
		    stringlist->append(TQString());
		    continue;
		}
		stringlist->append(*(qstringFromRString(item)));
	    }

	    m->item().s_voidp = stringlist;
	    m->next();

		
		if (stringlist != 0 && !m->type().isConst()) {
			rb_ary_clear(list);
			for(TQStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it)
		    	rb_ary_push(list, rstringFromTQString(&(*it)));
		}
			
		if (m->cleanup())
			delete stringlist;
	    break;
      }
      case Marshall::ToVALUE: 
	{
	    TQStringList *stringlist = static_cast<TQStringList *>(m->item().s_voidp);
	    if(!stringlist) {
		*(m->var()) = Qnil;
		break;
	    }

	    VALUE av = rb_ary_new();
	    for(TQStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it) {
		VALUE rv = rstringFromTQString(&(*it));
		rb_ary_push(av, rv);
	    }

	    if(m->cleanup())
		delete stringlist;

	    *(m->var()) = av;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQStrList(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE: 
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }

	    int count = RARRAY_LEN(list);
	    TQStrList *stringlist = new TQStrList;

	    for(long i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
		if(TYPE(item) != T_STRING) {
		    stringlist->append(TQString());
		    continue;
		}
		stringlist->append(TQString::fromUtf8(StringValuePtr(item), RSTRING_LEN(item)));
	    }

	    m->item().s_voidp = stringlist;
	    m->next();

		if (!m->type().isConst()) {
			rb_ary_clear(list);
			for(const char * it = stringlist->first(); it != 0; it = stringlist->next())
				rb_ary_push(list, rb_str_new2(it));
		}

		if (m->cleanup()) {
			delete stringlist;
	    }
	    break;
      }
      case Marshall::ToVALUE: 
	{
	    TQStrList *stringlist = static_cast<TQStrList *>(m->item().s_voidp);
	    if(!stringlist) {
		*(m->var()) = Qnil;
		break;
	    }

	    VALUE av = rb_ary_new();
		for(const char * it = stringlist->first(); it != 0; it = stringlist->next()) {
		VALUE rv = rb_str_new2(it);
		rb_ary_push(av, rv);
	    }

	    if(m->cleanup())
		delete stringlist;

	    *(m->var()) = av;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

template <class Item, class ItemList, class ItemListIterator, const char *ItemSTR >
void marshall_ItemList(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }
	    int count = RARRAY_LEN(list);
	    ItemList *cpplist = new ItemList;
	    long i;
	    for(i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
                // TODO do type checking!
		smokeruby_object *o = value_obj_info(item);
		if(!o || !o->ptr)
                    continue;
		void *ptr = o->ptr;
		ptr = o->smoke->cast(
		    ptr,				// pointer
		    o->classId,				// from
		    o->smoke->idClass(ItemSTR)	        // to
		);
		cpplist->append((Item*)ptr);
	    }

	    m->item().s_voidp = cpplist;
	    m->next();

		if (!m->type().isConst()) {
			rb_ary_clear(list);
			for(ItemListIterator it = cpplist->begin();
				it != cpplist->end();
				++it ) 
			{
				VALUE obj = getPointerObject((void*)(*it));
				rb_ary_push(list, obj);
			}
		}

		if (m->cleanup()) {
			delete cpplist;
	    }
	}
	break;
      case Marshall::ToVALUE:
	{
	    ItemList *valuelist = (ItemList*)m->item().s_voidp;
	    if(!valuelist) {
		*(m->var()) = Qnil;
		break;
	    }

	    VALUE av = rb_ary_new();

	    for(ItemListIterator it = valuelist->begin();
		it != valuelist->end();
		++it) {
		void *p = *it;

		if(m->item().s_voidp == 0) {
		    *(m->var()) = Qnil;
		    break;
		}

		VALUE obj = getPointerObject(p);
		if(obj == Qnil) {
		    smokeruby_object  * o = ALLOC(smokeruby_object);
		    o->smoke = m->smoke();
		    o->classId = m->smoke()->idClass(ItemSTR);
		    o->ptr = p;
		    o->allocated = false;
		    obj = set_obj_info(resolve_classname(o->smoke, o->classId, o->ptr), o);
		}
		rb_ary_push(av, obj);
            }

	    if(m->cleanup())
		delete valuelist;
	    else
	        *(m->var()) = av;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQValueListInt(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }
	    int count = RARRAY_LEN(list);
	    TQValueList<int> *valuelist = new TQValueList<int>;
	    long i;
	    for(i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
		if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) {
		    valuelist->append(0);
		    continue;
		}
		valuelist->append(NUM2INT(item));
	    }

	    m->item().s_voidp = valuelist;
	    m->next();

		if (!m->type().isConst()) {
			rb_ary_clear(list);
			for(TQValueListIterator<int> it = valuelist->begin();
				it != valuelist->end();
				++it)
				rb_ary_push(list, INT2NUM((int)*it));
		}

		if (m->cleanup()) {
			delete valuelist;
	    }
	}
	break;
      case Marshall::ToVALUE:
	{
	    TQValueList<int> *valuelist = (TQValueList<int>*)m->item().s_voidp;
	    if(!valuelist) {
		*(m->var()) = Qnil;
		break;
	    }

	    VALUE av = rb_ary_new();

	    for(TQValueListIterator<int> it = valuelist->begin();
		it != valuelist->end();
		++it)
		rb_ary_push(av, INT2NUM(*it));
		
	    *(m->var()) = av;
		
	    if(m->cleanup())
		delete valuelist;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_voidP(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE rv = *(m->var());
	    if (rv != Qnil)
		m->item().s_voidp = (void*)NUM2INT(*(m->var()));
	    else
		m->item().s_voidp = 0;
	}
	break;
      case Marshall::ToVALUE:
	{
	    *(m->var()) = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp);
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQMapTQStringTQString(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE hash = *(m->var());
	    if (TYPE(hash) != T_HASH) {
		m->item().s_voidp = 0;
		break;
	    }
		
		TQMap<TQString,TQString> * map = new TQMap<TQString,TQString>;
		
		// Convert the ruby hash to an array of key/value arrays
		VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0);

		for (long i = 0; i < RARRAY_LEN(temp); i++) {
			VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0);
			VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1);
			(*map)[TQString(StringValuePtr(key))] = TQString(StringValuePtr(value));
		}
	    
		m->item().s_voidp = map;
		m->next();
		
	    if(m->cleanup())
		delete map;
	}
	break;
      case Marshall::ToVALUE:
	{
	    TQMap<TQString,TQString> *map = (TQMap<TQString,TQString>*)m->item().s_voidp;
	    if(!map) {
		*(m->var()) = Qnil;
		break;
	    }
		
	    VALUE hv = rb_hash_new();
			
		TQMap<TQString,TQString>::Iterator it;
		for (it = map->begin(); it != map->end(); ++it) {
			rb_hash_aset(hv, rstringFromTQString((TQString*)&(it.key())), rstringFromTQString((TQString*) &(it.data())));
        }
		
		*(m->var()) = hv;
		m->next();
		
	    if(m->cleanup())
		delete map;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQMapTQStringTQVariant(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE hash = *(m->var());
	    if (TYPE(hash) != T_HASH) {
		m->item().s_voidp = 0;
		break;
	    }
		
		TQStringVariantMap * map = new TQStringVariantMap;
		
		// Convert the ruby hash to an array of key/value arrays
		VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0);

		for (long i = 0; i < RARRAY_LEN(temp); i++) {
			VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0);
			VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1);
			
			smokeruby_object *o = value_obj_info(value);
			if (!o || !o->ptr || o->classId != o->smoke->idClass("TQVariant")) {
				// If the value isn't a TQt::Variant, then try and construct
				// a TQt::Variant from it
				value = rb_funcall(qvariant_class, rb_intern("new"), 1, value);
				if (value == Qnil) {
					continue;
				}
				o = value_obj_info(value);
			}

			void * ptr = o->ptr;
			ptr = o->smoke->cast(ptr, o->classId, o->smoke->idClass("TQVariant"));
			
			(*map)[TQString(StringValuePtr(key))] = (TQVariant)*(TQVariant*)ptr;
		}
	    
		m->item().s_voidp = map;
		m->next();
		
	    if(m->cleanup())
		delete map;
	}
	break;
      case Marshall::ToVALUE:
	{
	    TQStringVariantMap *map = (TQStringVariantMap*)m->item().s_voidp;
	    if(!map) {
		*(m->var()) = Qnil;
		break;
	    }
		
	    VALUE hv = rb_hash_new();
			
		TQStringVariantMap::Iterator it;
		for (it = map->begin(); it != map->end(); ++it) {
			void *p = new TQVariant(it.data());
			VALUE obj = getPointerObject(p);
				
			if (obj == Qnil) {
				smokeruby_object  * o = ALLOC(smokeruby_object);
				o->classId = m->smoke()->idClass("TQVariant");
				o->smoke = m->smoke();
				o->ptr = p;
				o->allocated = true;
				obj = set_obj_info("TQt::Variant", o);
			}
			
			rb_hash_aset(hv, rstringFromTQString((TQString*)&(it.key())), obj);
        }
		
		*(m->var()) = hv;
		m->next();
		
	    if(m->cleanup())
		delete map;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQUObject(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE array = *(m->var());
	    if (array != Qnil && TYPE(array) == T_ARRAY) {
		VALUE rv = rb_ary_entry(array, 0);
		Data_Get_Struct(rv, TQUObject, m->item().s_voidp);
	    } else {
		m->item().s_voidp = 0;
		}
	}
	break;
      case Marshall::ToVALUE:
	{
	    VALUE rv = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp);
		VALUE array = rb_ary_new2(1);
		rb_ary_push(array, rv);
	    *(m->var()) = array;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQRgb_array(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }
	    int count = RARRAY_LEN(list);
	    TQRgb *rgb = new TQRgb[count + 2];
	    long i;
	    for(i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
		if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) {
		    rgb[i] = 0;
		    continue;
		}

		rgb[i] = NUM2UINT(item);
	    }
	    m->item().s_voidp = rgb;
	    m->next();
	}
	break;
      case Marshall::ToVALUE:
	// Implement this with a tied array or something
      default:
	m->unsupported();
	break;
    }
}

void marshall_TQPairintint(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY || RARRAY_LEN(list) != 2) {
		m->item().s_voidp = 0;
		break;
	    }
		int int0;
		int int1;
		VALUE item = rb_ary_entry(list, 0);
		if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) {
		    int0 = 0;
		} else {
			int0 = NUM2INT(item);
		}
		
		item = rb_ary_entry(list, 1);
		if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) {
		    int1 = 0;
		} else {
			int1 = NUM2INT(item);
		}
		
		TQPair<int,int> * qpair = new TQPair<int,int>(int0,int1);
	    m->item().s_voidp = qpair;
	    m->next();
	    if(m->cleanup())
		delete qpair;
	}
	break;
      case Marshall::ToVALUE:
      default:
	m->unsupported();
	break;
    }
}

#define DEF_LIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace { char ListIdent##STR[] = #Item; };  \
        Marshall::HandlerFn marshall_##ListIdent = marshall_ItemList<Item,ItemList,Itr,ListIdent##STR>;

#include <tqcanvas.h>
#include <tqdir.h>
#include <tqobjectlist.h>
#include <tqwidgetlist.h>
#include <tqdockwindow.h>
#include <tqnetworkprotocol.h>
#include <tqtoolbar.h>
#include <tqtabbar.h>

#if TQT_VERSION >= 0x030200
DEF_LIST_MARSHALLER( TQPtrListTQNetworkOperation, TQPtrList<TQNetworkOperation>, TQNetworkOperation, TQPtrListStdIterator<TQNetworkOperation> )
DEF_LIST_MARSHALLER( TQPtrListTQToolBar, TQPtrList<TQToolBar>, TQToolBar, TQPtrListStdIterator<TQToolBar> )
DEF_LIST_MARSHALLER( TQPtrListTQTab, TQPtrList<TQTab>, TQTab, TQPtrListStdIterator<TQTab> )
DEF_LIST_MARSHALLER( TQPtrListTQDockWindow, TQPtrList<TQDockWindow>, TQDockWindow, TQPtrListStdIterator<TQDockWindow> )
DEF_LIST_MARSHALLER( TQFileInfoList, TQFileInfoList, TQFileInfo, TQFileInfoList::Iterator )
DEF_LIST_MARSHALLER( TQObjectList, TQObjectList, TQObject, TQPtrListStdIterator<TQObject> )
DEF_LIST_MARSHALLER( TQWidgetList, TQWidgetList, TQWidget, TQPtrListStdIterator<TQWidget> )
#endif

DEF_LIST_MARSHALLER( TQCanvasItemList, TQCanvasItemList, TQCanvasItem, TQValueListIterator<TQCanvasItem*> )

template <class Item, class ItemList, class ItemListIterator, const char *ItemSTR >
void marshall_ValueItemList(Marshall *m) {
    switch(m->action()) {
      case Marshall::FromVALUE:
	{
	    VALUE list = *(m->var());
	    if (TYPE(list) != T_ARRAY) {
		m->item().s_voidp = 0;
		break;
	    }
	    int count = RARRAY_LEN(list);
	    ItemList *cpplist = new ItemList;
	    long i;
	    for(i = 0; i < count; i++) {
		VALUE item = rb_ary_entry(list, i);
                // TODO do type checking!
		smokeruby_object *o = value_obj_info(item);

		// Special case for the TQValueList<TQVariant> type
		if (	tqstrcmp(ItemSTR, "TQVariant") == 0 
				&& (!o || !o->ptr || o->classId != o->smoke->idClass("TQVariant")) ) 
		{
			// If the value isn't a TQt::Variant, then try and construct
			// a TQt::Variant from it
			item = rb_funcall(qvariant_class, rb_intern("new"), 1, item);
			if (item == Qnil) {
				continue;
			}
			o = value_obj_info(item);
		}

		if(!o || !o->ptr)
                    continue;
		void *ptr = o->ptr;
		ptr = o->smoke->cast(
		    ptr,				// pointer
		    o->classId,				// from
		    o->smoke->idClass(ItemSTR)	        // to
		);
		cpplist->append(*(Item*)ptr);
	    }

	    m->item().s_voidp = cpplist;
	    m->next();

		if (!m->type().isConst()) {
			rb_ary_clear(list);
			for(ItemListIterator it = cpplist->begin();
				it != cpplist->end();
				++it) 
			{
				VALUE obj = getPointerObject((void*)&(*it));
				rb_ary_push(list, obj);
			}
		}

		if (m->cleanup()) {
			delete cpplist;
	    }
	}
	break;
      case Marshall::ToVALUE:
	{
	    ItemList *valuelist = (ItemList*)m->item().s_voidp;
	    if(!valuelist) {
		*(m->var()) = Qnil;
		break;
	    }

	    VALUE av = rb_ary_new();

	    int ix = m->smoke()->idClass(ItemSTR);
	    const char * className = m->smoke()->binding->className(ix);

	    for(ItemListIterator it = valuelist->begin();
		it != valuelist->end();
		++it) {
		void *p = &(*it);

		if(m->item().s_voidp == 0) {
		    *(m->var()) = Qnil;
		    break;
		}

		VALUE obj = getPointerObject(p);
		if(obj == Qnil) {
		    smokeruby_object  * o = ALLOC(smokeruby_object);
		    o->smoke = m->smoke();
		    o->classId = o->smoke->idClass(ItemSTR);
		    o->ptr = p;
		    o->allocated = false;
		    obj = set_obj_info(className, o);
		}
		rb_ary_push(av, obj);
            }

	    if(m->cleanup())
		delete valuelist;
	    else
	        *(m->var()) = av;
	}
	break;
      default:
	m->unsupported();
	break;
    }
}

#define DEF_VALUELIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace dummy { char ListIdent##STR[] = #Item; };  \
        Marshall::HandlerFn marshall_##ListIdent = marshall_ValueItemList<Item,ItemList,Itr,dummy::ListIdent##STR>;

DEF_VALUELIST_MARSHALLER( TQVariantList, TQValueList<TQVariant>, TQVariant, TQValueList<TQVariant>::Iterator )
DEF_VALUELIST_MARSHALLER( TQPixmapList, TQValueList<TQPixmap>, TQPixmap, TQValueList<TQPixmap>::Iterator )
DEF_VALUELIST_MARSHALLER( TQIconDragItemList, TQValueList<TQIconDragItem>, TQIconDragItem, TQValueList<TQIconDragItem>::Iterator )
DEF_VALUELIST_MARSHALLER( TQImageTextKeyLangList, TQValueList<TQImageTextKeyLang>, TQImageTextKeyLang, TQValueList<TQImageTextKeyLang>::Iterator )
DEF_VALUELIST_MARSHALLER( TQUrlInfoList, TQValueList<TQUrlInfo>, TQUrlInfo, TQValueList<TQUrlInfo>::Iterator )
DEF_VALUELIST_MARSHALLER( TQTranslatorMessageList, TQValueList<TQTranslatorMessage>, TQTranslatorMessage, TQValueList<TQTranslatorMessage>::Iterator )
DEF_VALUELIST_MARSHALLER( TQHostAddressList, TQValueList<TQHostAddress>, TQHostAddress, TQValueList<TQHostAddress>::Iterator )

TypeHandler Qt_handlers[] = {
    { "TQString", marshall_TQString },
    { "TQString&", marshall_TQString },
    { "TQString*", marshall_TQString },
    { "TQCString", marshall_TQCString },
    { "TQCString&", marshall_TQCString },
    { "TQCString*", marshall_TQCString },
    { "TQStringList", marshall_TQStringList },
    { "TQStringList&", marshall_TQStringList },
    { "TQStringList*", marshall_TQStringList },
    { "TQStrList", marshall_TQStrList },
    { "TQStrList&", marshall_TQStrList },
    { "TQStrList*", marshall_TQStrList },
    { "long long int", marshall_longlong },
    { "long long int&", marshall_longlong },
    { "TQ_INT64", marshall_longlong },
    { "TQ_INT64&", marshall_longlong },
    { "TQ_LLONG", marshall_longlong },
    { "TQ_LLONG&", marshall_longlong },
    { "TDEIO::filesize_t", marshall_longlong },
    { "DOM::DOMTimeStamp", marshall_ulonglong },
    { "unsigned long long int", marshall_ulonglong },
    { "unsigned long long int&", marshall_ulonglong },
    { "TQ_UINT64", marshall_ulonglong },
    { "TQ_UINT64&", marshall_ulonglong },
    { "TQ_ULLONG", marshall_ulonglong },
    { "TQ_ULLONG&", marshall_ulonglong },
    { "signed int&", marshall_intR },
    { "int&", marshall_intR },
    { "int*", marshall_intR },
    { "bool&", marshall_boolR },
    { "bool*", marshall_boolR },
    { "char*", marshall_charP },
    { "char**", marshall_charP_array },
    { "uchar*", marshall_ucharP },
    { "TQRgb*", marshall_TQRgb_array },
    { "TQPair<int,int>&", marshall_TQPairintint },
    { "TQUObject*", marshall_TQUObject },
    { "const TQCOORD*", marshall_TQCOORD_array },
    { "void", marshall_void },
    { "TQValueList<int>", marshall_TQValueListInt },
    { "TQValueList<int>&", marshall_TQValueListInt },
    { "TQValueList<TQVariant>", marshall_TQVariantList },
    { "TQValueList<TQVariant>&", marshall_TQVariantList },
    { "TQValueList<TQPixmap>", marshall_TQPixmapList },
    { "TQValueList<TQIconDragItem>&", marshall_TQIconDragItemList },
    { "TQValueList<TQImageTextKeyLang>", marshall_TQImageTextKeyLangList },
    { "TQValueList<TQUrlInfo>&", marshall_TQUrlInfoList },
    { "TQValueList<TQTranslatorMessage>", marshall_TQTranslatorMessageList },
    { "TQValueList<TQHostAddress>", marshall_TQHostAddressList },
    { "TQCanvasItemList", marshall_TQCanvasItemList },
    { "TQMap<TQString,TQString>", marshall_TQMapTQStringTQString },
    { "TQMap<TQString,TQString>&", marshall_TQMapTQStringTQString },
    { "TQStringVariantMap", marshall_TQMapTQStringTQVariant },
    { "TQStringVariantMap&", marshall_TQMapTQStringTQVariant },
#if TQT_VERSION >= 0x030200
    { "TQWidgetList", marshall_TQWidgetList },
    { "TQWidgetList*", marshall_TQWidgetList },
    { "TQWidgetList&", marshall_TQWidgetList },
    { "TQObjectList*", marshall_TQObjectList },
    { "TQObjectList&", marshall_TQObjectList },
    { "TQFileInfoList*", marshall_TQFileInfoList },
    { "TQPtrList<TQToolBar>", marshall_TQPtrListTQToolBar },
    { "TQPtrList<TQTab>*", marshall_TQPtrListTQTab },
    { "TQPtrList<TQDockWindow>", marshall_TQPtrListTQDockWindow },
    { "TQPtrList<TQDockWindow>*", marshall_TQPtrListTQDockWindow },
    { "TQPtrList<TQNetworkOperation>", marshall_TQPtrListTQNetworkOperation },
    { "TQPtrList<TQNetworkOperation>&", marshall_TQPtrListTQNetworkOperation },
#endif
    { 0, 0 }
};

TQAsciiDict<TypeHandler> type_handlers(199);

void install_handlers(TypeHandler *h) {
    while(h->name) {
	type_handlers.insert(h->name, h);
	h++;
    }
}

Marshall::HandlerFn getMarshallFn(const SmokeType &type) {
    if(type.elem())
	return marshall_basetype;
    if(!type.name())
	return marshall_void;
	TypeHandler *h = type_handlers[type.name()];
    if(h == 0 && type.isConst() && strlen(type.name()) > strlen("const ")) {
    	h = type_handlers[type.name() + strlen("const ")];
    }
	
    if(h != 0) {
	return h->fn;
    }

    return marshall_unknown;
}
