/****************************************************************************
** $Id: quickinterpreter.cpp  beta1   edited Dec 17 16:01 $
**
** Copyright (C) 2001-2002 Trolltech AS.  All rights reserved.
**
** This file is part of the Qt Script for Applications framework (QSA).
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding a valid QSA Beta Evaluation Version license may use
** this file in accordance with the QSA Beta Evaluation Version License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about QSA Commercial License Agreements.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
*****************************************************************************/

#include "quickinterpreter.h"

#include "quickobjects.h"
#include "quickdebugger.h"
#include "quickdispatchobject.h"
#include "quickbytearrayobject.h"
#include "quickpixmapobject.h"
#include "quickcolorobject.h"
#include "quickfontobject.h"
#include "quickcoordobjects.h"
#include "quickobjects.h"
#include "qsenv.h"
#include "qsfunction.h"
#include "qsfuncref.h"
#include <qobjectlist.h>
#include <qmetaobject.h>
#include <qapplication.h>
#include <qdom.h>
#include <private/qucomextra_p.h>
#include <qregexp.h>
#include <qdir.h>
#include <qwidgetfactory.h>
#include <quickextensions.h>
#include <qsclass.h>
#include <qsnodes.h>
#include <qvaluevector.h>

static bool eTimers = TRUE;
QuickInterpreter *QuickInterpreter::selfPtr = 0;
static int static_id = 0;

void QUICKCORE_EXPORT qsa_throw_error( const QString &message )
{
    QString msg( message );
    if ( !msg.startsWith( "Error:" ) )
	msg.prepend( "Error:" );
    QSEnv::current()->throwError( TypeError, msg, -1 );
}


static QPtrList<QuickDispatchObjectFactory> *dispatchObjFactories = 0;

////////////////////////////////////////////////////////////////////

static QSObject qsConnectCommon( const QSList &args,
				 QSObject &arg0,
				 QSObject &arg2,
				 QSWrapperShared *&sendObj,
				 QSWrapperShared *&recObj,
				 const QPtrVector<QObject> *&sendIfaces,
				 const QPtrVector<QObject> *&recIfaces,
				 QString &sig,
				 QString &sl,
				 int &signal_index,
				 QObject *&sender,
				 const QString &func )
{
    QSEnv *env = QSEnv::current();

    const QString overloads =
	QString( "Following overloads are possible:\n"
		 "%1( sender : QObject, signal : String, receiver : QObject, slot : String )\n"
		 "%2( sender : QObject, signal : String, function : DeclaredFunction )" ).
	arg( func ).arg( func );

    if ( args.size() < 3 || args.size() > 4 ) {
	QString msg = "No matching overload found. " + overloads;
	return env->throwError( SyntaxError, msg );
    }

    QSWrapperClass *wClass = QuickInterpreter::self()->wrapperClass();
    if ( !args[0].isA( wClass ) ) {
	QString msg = "No matching overload found. 'sender' must be of type QObject but is of type " +
		      args[0].typeName() + "\n" + overloads;
	return env->throwError( TypeError, msg );
    }

    if ( args.size() == 3 && !args[2].isFunction() ) {
	QString msg = "No matching overloads found. Third argument in this overload "
		      "must be of type function but is of type " + args[2].typeName() + "\n" + overloads;
	return env->throwError( TypeError, msg );
    }

    arg0 = args[0];
    arg2 = args[2];
    sendObj = wClass->shared( &arg0 );
    recObj =  arg2.isA( wClass ) ? wClass->shared( &arg2 ) : 0;
    sendIfaces = sendObj->interfaceObjects();
    recIfaces = recObj ? recObj->interfaceObjects() : 0;

    // signal and slot in string representation
    sig = args[1].toString();
    sl = args[3].toString();

    // find sender and signal
    sender = 0;
    signal_index = -1;
    const char *sigName = sig.ascii();
    int i;
    for ( i = (int)sendIfaces->count()-1; i >= 0; --i ) {
	sender = sendIfaces->at( i );
	signal_index = sender->metaObject()->findSignal( sigName, (i == 0) );
	if ( signal_index > 0 )
	    break;
    }

    if ( signal_index == -1 ) {
	// didn't find the signal- this signature might be in Qt
	// Script syntax, so lets's try to find out if we can connect
	QString sn = sig;
	QString arg = sig.mid( sig.find( '(' ) + 1 );
	arg = arg.left( arg.findRev( ')' ) );
	QStringList args = QStringList::split( ',', arg );
	sn = sig.left( sig.find( '(' ) );
	for ( i = (int)sendIfaces->count()-1; i >= 0; --i ) {
	    sender = sendIfaces->at( i );
	    for ( int j = 0; j < (int)sender->metaObject()->numSignals( TRUE ); ++j ) {
		const QMetaData *md = sender->metaObject()->signal( j, TRUE );
		QString mdn = md->name;
		mdn = mdn.left( mdn.find( '(' ) );
		if ( mdn != sn )
		    continue;
		const QUMethod *method = md->method;
		bool ok = method->count == (int)args.count();
		for ( int k = 0; k < method->count; ++k ) {
		    QUParameter p = method->parameters[k];
		    QString s = *args.at( k );
		    int sep;
		    if ( ( sep = s.find( ':' ) ) != -1 )
			s = s.mid( sep + 1 );
		    s = s.simplifyWhiteSpace();
		    if ( s == p.type->desc() || s == QString( (const char*)p.typeExtra ) )
			continue;
		    if ( s == "Number" &&
			 ( qstrcmp( p.type->desc(), "int" ) == 0 ||
			   qstrcmp( p.type->desc(), "long" ) == 0 ||
			   qstrcmp( p.type->desc(), "double" ) == 0 ||
			   qstrcmp( p.type->desc(), "float" ) == 0 ||
			   qstrcmp( p.type->desc(), "short" ) == 0 ||
			   qstrcmp( p.type->desc(), "uint" ) == 0 ||
			   qstrcmp( p.type->desc(), "ushort" ) == 0 ||
			   qstrcmp( p.type->desc(), "ulong" ) == 0 ||
			   qstrcmp( p.type->desc(), "unsigned int" ) == 0 ||
			   qstrcmp( p.type->desc(), "unsigned short" ) == 0 ||
			   qstrcmp( p.type->desc(), "unsigned long" ) == 0 ) )
			continue;
		    s.prepend( "Q" );
		    if ( s == p.type->desc() || s == QString( (const char*)p.typeExtra ) )
			continue;
		    ok = FALSE;
		    break;
		}
		if ( !ok )
		    continue;
		signal_index = j;
		sig = sender->metaObject()->signal( j, TRUE )->name;
		break;
	    }
	    if ( signal_index != -1 )
		break;
	}
    }

    return QSObject();
}

#define QS_CONNECT_INIT( func ) \
    QSObject arg0; \
    QSObject arg2; \
    QSWrapperShared *sendObj; \
    QSWrapperShared *recObj; \
    const QPtrVector<QObject> *sendIfaces = 0; \
    const QPtrVector<QObject> *recIfaces = 0; \
    QString sig; \
    QString sl; \
    int signal_index; \
    QObject *sender = 0; \
    QSObject ret = qsConnectCommon( args, arg0, arg2, sendObj, recObj, \
				    sendIfaces, recIfaces, sig, sl, signal_index, \
				    sender, #func ); \
    if ( ret.isValid() ) { \
	return ret; }


static QSObject qsConnect( const QSList &args )
{
    QS_CONNECT_INIT( connect );

    // find receiver and slot
    QObject *receiver = 0;
    int member_index = -1;
    const char *slotName = sl.ascii();
    if ( recIfaces ) {
	for ( int i = (int)recIfaces->count()-1; i >= 0; --i ) {
	    receiver = recIfaces->at( i );
	    member_index = receiver->metaObject()->findSlot( slotName, (i == 0) );
	    if ( member_index > 0 && signal_index > 0 ) {
		// regular signal/slot connection
		QObject::connectInternal( sender, signal_index,
					  receiver, QSLOT_CODE, member_index );
		return QSUndefined();
	    }
	}
    }

    if ( signal_index == -1 ) {
	QString msg = "Can't find signal named " + sig;
	return QSEnv::current()->throwError( SyntaxError, msg );
    }

    if ( recIfaces ) {
	sendObj->setEventHandler( sig, recIfaces->at( 0 ), sl.left( sl.find( '(' ) ) );
    } else {
	QSObject base = QSFuncRefClass::refBase( arg2 );
	QSMember member = QSFuncRefClass::refMember( arg2 );
	sendObj->setEventHandler( sig, 0, member.name(), base );
    }

    return QSUndefined();
}

static QSObject qsDisconnect( const QSList &args )
{
    QS_CONNECT_INIT( disconnect );

    // find receiver and slot
    QObject *receiver = 0;
    int member_index = -1;
    const char *slotName = sl.ascii();
    if ( recIfaces ) {
	for ( int i = (int)recIfaces->count()-1; i >= 0; --i ) {
	    receiver = recIfaces->at( i );
	    member_index = receiver->metaObject()->findSlot( slotName, (i == 0) );
	    if ( member_index > 0 && signal_index > 0 ) {
		// regular signal/slot connection
		QObject::disconnectInternal( sender, signal_index, receiver,
					     QSLOT_CODE, member_index );
		return QSUndefined();
	    }
	}
    }

    if ( signal_index == -1 ) {
	QString msg = "Can't find signal named " + sig;
	return QSEnv::current()->throwError( SyntaxError, msg );
    }

    if ( recIfaces ) {
	sendObj->removeEventHandler( sig, recIfaces->at( 0 ), sl.left( sl.find( '(' ) ) );
    } else {
	QSObject base = QSFuncRefClass::refBase( arg2 );
	QSMember member = QSFuncRefClass::refMember( arg2 );
	sendObj->removeEventHandler( sig, 0, member.name(), base );
    }

    return QSUndefined();
}

////////////////////////////////////////////////////////////////////

QuickInterpreter::QuickInterpreter( bool deb )
    : toplevel( 0 )
{
    shuttingDown = FALSE;
    id = static_id++;
    wrapperShared = new QSWrapperSharedList;
    wrapperShared->setAutoDelete( TRUE );
    usrDataId = QObject::registerUserData();
    globalObjects.setAutoDelete( TRUE );

    engine = new QSEngine( this );
    debugger = deb ? new QuickDebugger( engine ) : 0;
    connect( engine, SIGNAL( warning( const QString &, int ) ),
	     this, SIGNAL( warning( const QString &, int ) ) );
    if ( !selfPtr )
	selfPtr = this;
    init();
}

QuickInterpreter::~QuickInterpreter()
{
    shuttingDown = TRUE;
    stopAllTimers();
    invalidateWrappers();
    delete toplevel;
    delete debugger;
    if ( selfPtr == this )
	selfPtr = 0;
    delete engine;
    Q_ASSERT( wrapperShared->isEmpty() );
    delete wrapperShared;
}

QuickInterpreter *QuickInterpreter::self()
{
    if ( !selfPtr )
	selfPtr = new QuickInterpreter( TRUE );

    return selfPtr;
}

void QuickInterpreter::reinit()
{
    engine->init();
}

void QuickInterpreter::enableTimers( bool b )
{
    eTimers = b;
}

bool QuickInterpreter::timersEnabled()
{
    return eTimers;
}

QSEnv *QuickInterpreter::env() const
{
    return engine->env();
}

QSObject QuickInterpreter::wrap( QObject *o )
{
    QSUserData *udata = (QSUserData*) o->userData( userDataId() );
    // set user data in QObject if it's not there, yet
    if( !udata ) {
	udata = new QSUserData( 0 );
	o->setUserData( userDataId(), udata );
    }
    QSWrapperShared *shared = udata->data();
    const QSWrapperClass *cl;
    if ( shared ) {
	// wrapper is already there, reuse it
	cl = shared->wrapperClass();
    } else {
	// create & remember wrapper
	cl = new QSWrapperClass( wrapperClass() );
	shared = cl->createShared( o );
	wrapperShared->append( shared );
    }
    shared->setUserData( udata );
    udata->setData( shared );
    shared->ref(); // additional ref by either QObject or QSObject
    return QSObject( cl, shared );
}

void QuickInterpreter::init()
{
    if ( !dispatchObjFactories ) {
	dispatchObjFactories = new QPtrList<QuickDispatchObjectFactory>;
	addDispatchObjectFactory( new QuickExtensions );
    }

    staticGlobals.clear();

    staticGlobals << "NaN" << "undefined" << "Infinity" << "Application";

    // add some common objects to the Global object
    QSObject global( env()->globalObject() );
    QSClass *objClass = env()->objectClass();

    wrpClass = new QSWrapperClass( objClass );
    ptrClass = new QSPointerClass( objClass );
    varClass = new QSVariantClass( objClass );
    appClass = new QSApplicationClass( objClass );
    global.put( "Application", appClass->createWritable() );

    pntClass = new QSPointClass( objClass );
    engine->registerType( pntClass );
    sizClass = new QSSizeClass( objClass );
    engine->registerType( sizClass );
    rctClass = new QSRectClass( objClass );
    engine->registerType( rctClass );
    colClass = new QSColorClass( objClass );
    engine->registerType( colClass );
    fntClass = new QSFontClass( objClass );
    engine->registerType( fntClass );
    baClass = new QSByteArrayClass( objClass );
    engine->registerType( baClass );
    pixClass = new QSPixmapClass( objClass );
    engine->registerType( pixClass );

    engine->enableDebug(); // adds "debug" function which uses qDebug()
    env()->globalClass()->addMember( "connect", QSMember( &qsConnect ) );
    env()->globalClass()->addMember( "disconnect", QSMember( &qsDisconnect ) );
    env()->globalClass()->addMember( "startTimer", QSMember( &qsStartTimer ) );
    env()->globalClass()->addMember( "killTimer", QSMember( &qsKillTimer ) );
    env()->globalClass()->addMember( "killTimers", QSMember( &qsKillTimers ) );

    for ( QuickDispatchObjectFactory *f = dispatchObjFactories->first(); f; f = dispatchObjFactories->next() ) {
	QStringList features = f->featureList();
	for ( QStringList::ConstIterator it = features.begin();
	      it != features.end(); ++it ) {
	    if ( f->isConstructor( *it ) ) {
		QSClass *c = new QSObjectConstructor( objClass, *it );
		engine->registerType( c );
		continue;
	    }
	    if ( !f->isGlobal( (*it).latin1() ) )
		continue;

	    QPtrVector<QObject> objects;
	    f->createInterface( (*it).latin1(), 0, objects );
	    QSObject obj = wrapperClass()->wrap( objects );
	    env()->globalClass()->addStaticVariableMember( *it, obj, AttributeNone );
	    staticGlobals << *it;
	    // register new'ed QObject for deletion as the wrapper
	    // doesn't take ownership of the wrapped QObject
	    globalObjects.append( objects[0] );
	}
    }
}

bool QuickInterpreter::hasTopLevelParent( QObject *o )
{
    for ( QObject *p = toplevel->first(); p; p = toplevel->next() ) {
	QObject *c = p->child( o->name(), o->className() );
	if ( c )
	    return TRUE;
    }
    return FALSE;
}

void QuickInterpreter::addTopLevelObject( QObject *o )
{
    engine->init();
    if ( !toplevel )
	toplevel = new QObjectList;
    if ( toplevel->findRef( o ) != -1 )
	return;
    if ( hasTopLevelParent( o ) )
	return;
    toplevel->append( o );

    kids.clear();
    if ( !toplevel )
	return;
    QObject *obj = toplevel->first();
    while ( obj ) {
	kids.append( obj->name() );
	obj = toplevel->next();
    }
    connect( o, SIGNAL( destroyed( QObject * ) ), this, SLOT( topLevelDestroyed( QObject * ) ) );

    env()->globalObject().put( o->name(), wrap( o ) );
    staticGlobals << o->name();
}

void QuickInterpreter::setTopLevelObjects( QObjectList *l )
{
    engine->init();
    delete toplevel;
    toplevel = new QObjectList;

    kids.clear();
    if ( !l )
	return;
    QSObject global( env()->globalObject() );
    QObject *o = l->first();
    while ( o ) {
	if ( hasTopLevelParent( o ) ) {
	    o = l->next();
	    continue;
	}

	kids.append( o->name() );
	connect( o, SIGNAL( destroyed( QObject * ) ), this, SLOT( topLevelDestroyed( QObject * ) ) );
	global.put( o->name(), wrap( o ) );
	staticGlobals << o->name();
	toplevel->append( o );
	o = l->next();
    }
    delete l;
}

void QuickInterpreter::topLevelDestroyed( QObject *o )
{
    toplevel->removeRef( o );
}

bool QuickInterpreter::checkSyntax( const QString &c )
{
    QString code = c + "\n";
    return engine->checkSyntax( code );
}

QVariant QuickInterpreter::execute( QObject *obj, const QString &c, int &sourceId )
{
    QString code = c + "\n";

    sourceId = debugger ? debugger->freeSourceId() : -1;
    QObject *_this;
    if ( !obj || obj->inherits( "SourceFile" ) ) {	// put into global context
	_this = 0;
    } else {
	_this = obj;
	addTopLevelObject( obj );
    }
    addSourceId( sourceId, obj );

    QSObject t, oldThis;
    if ( _this ) {
	t = wrap( _this );
	oldThis = env()->thisValue();
	env()->setThisValue( t );
    }

    engine->evaluate( t, code );

    // restore this value
    if ( _this )
	env()->setThisValue( oldThis );

    if (hadError())
	emit runtimeError();

    return qsToVariant( engine->returnValue(), QVariant::Invalid );
}

QVariant QuickInterpreter::call( QSObject ctx, const QString &func,
				 const QSList &args )
{
    if ( shuttingDown || !engine )
	return QVariant();

    engine->call( &ctx, func, args );

    if (hadError())
	emit runtimeError();

    return qsToVariant( QSObject( engine->returnValue() ), QVariant::Invalid );
}

QVariant QuickInterpreter::call( QObject *ctx, const QString &func,
				 const QSList &args )
{
    if ( shuttingDown || !engine )
	return QVariant();

    QSObject t;
    if ( ctx )
	t = wrap( ctx );

    engine->call( &t, func, args );

    if (hadError())
	emit runtimeError();

    return qsToVariant( QSObject( engine->returnValue() ), QVariant::Invalid );
}

QVariant QuickInterpreter::call( QObject *ctx, const QString &func, const QValueList<QVariant> &args )
{
    QSList l;
    for ( QValueList<QVariant>::ConstIterator it = args.begin(); it != args.end(); ++it )
	l.append( variantClass()->construct( *it, 0 ) );
    return call( ctx, func, l );
}

void QuickInterpreter::invalidateWrappers()
{
    wrapperShared->clear();
}

void QuickInterpreter::clear()
{
    invalidateWrappers();
    engine->clear();
    engine->init();
    init();
    stopAllTimers();
}

void QuickInterpreter::stop()
{
    stopAllTimers();
}

bool QuickInterpreter::hadError() const
{
    return !!engine->errorType();
}

QStringList QuickInterpreter::errorMessages() const
{
    return engine->errorMessages();
}

QValueList<uint> QuickInterpreter::errorLines() const
{
    return engine->errorLines();
}

/*!
  Emit warning message \a msg for line \a l.
*/

void QuickInterpreter::warn( const QString &msg, int l )
{
    emit warning( msg, l );
}

void QuickInterpreter::clearSourceIdMap()
{
    sourceIdMap.clear();
}

void QuickInterpreter::addSourceId( int id, QObject *o )
{
    sourceIdMap.insert( id, o );
}

QObject *QuickInterpreter::objectOfSourceId( int id ) const
{
    QMap<int, QObject*>::ConstIterator it = sourceIdMap.find( id );
    if ( it == sourceIdMap.end() )
	return 0;
    return *it;
}

int QuickInterpreter::sourceIdOfObject( QObject *o ) const
{
    for ( QMap<int, QObject*>::ConstIterator it = sourceIdMap.begin(); it != sourceIdMap.end(); ++it ) {
	if ( *it == o )
	    return it.key();
    }
    return -1;
}

void QuickInterpreter::cleanType( QString &type )
{
    type = type.simplifyWhiteSpace();
    if ( type.left( 5 ) == "const" )
	type.remove( 0, 5 );
    if ( type[ (int)type.length() - 1 ] == '&' ||
	 type[ (int)type.length() - 1 ] == '*' )
	type.remove( type.length() - 1, 1 );
    type = type.simplifyWhiteSpace();
    if ( type == "QString" )
	type = "String";
    else if ( type == "int" || type == "uint" ||
	      type == "long" || type == "ulong" ||
	      type == "double" || type == "float" )
	type = "Number";
    else if ( type == "bool" )
	type = "Boolean";
}

void QuickInterpreter::cleanTypeRev( QString &type )
{
    if ( type == "String" )
	type = "QString";
    else if ( type == "Number" )
	type = "double";
    else if ( type == "Boolean" )
	type = "bool";
}

bool QuickInterpreter::queryDispatchObjects( QObject *obj,
					     QPtrVector<QObject> &result )
{
    Q_ASSERT( obj );
    QMetaObject *meta = obj->metaObject();
    result.resize( 1 );
    result.insert( 0, obj );
    while ( meta ) {
	QPtrList<QuickDispatchObjectFactory> ifaces =
	    self()->findDispatchObjectFactory( meta->className() );
	if ( ifaces.isEmpty() ) {
	    meta = meta->superClass();
	    continue;
	}
	for ( QuickDispatchObjectFactory *iface = ifaces.first(); iface; iface = ifaces.next() )
	    iface->createInterface( meta->className(), (void*)obj, result );
	meta = meta->superClass();
    }

    return TRUE;
}

bool QuickInterpreter::queryDispatchObjects( const QCString &name, void *ptr,
					     QPtrVector<QObject> &result )
{
    QPtrList<QuickDispatchObjectFactory> ifaces = self()->findDispatchObjectFactory( name );
    if ( ifaces.isEmpty() )
	return FALSE;
    for ( QuickDispatchObjectFactory *iface = ifaces.first(); iface; iface = ifaces.next() )
	iface->createInterface( name, ptr, result );
    return TRUE;
}

bool QuickInterpreter::construct( const QString &className,
				  const QValueList<QVariant> &vargs,
				  QPtrVector<QObject> &result )
{
    QPtrList<QuickDispatchObjectFactory> ifaces = self()->findDispatchObjectFactory( className );
    if ( ifaces.isEmpty() )
	return FALSE;
    for ( QuickDispatchObjectFactory *iface = ifaces.first(); iface; iface = ifaces.next() )
	iface->construct( className, vargs, result );
    return TRUE;
}

QStringList QuickInterpreter::classes() const
{
    QPtrList<QSClass> clsLst = env()->classes();
    QStringList lst;
    for ( QSClass *cls = clsLst.first(); cls; cls = clsLst.next() ) {
	if ( cls->asClass() )
	    lst << cls->asClass()->identifier();
    }
    return lst;
}

void QuickInterpreter::addDispatchObjectFactory( QuickDispatchObjectFactory *f )
{
    dispatchObjFactories->append( f );
    factoryCache.clear();
}

QPtrList<QuickDispatchObjectFactory> QuickInterpreter::findDispatchObjectFactory( const QString &feature )
{
    QMap<QString, QPtrList<QuickDispatchObjectFactory> >::Iterator it = factoryCache.find( feature );
    if ( it != factoryCache.end() )
	return *it;
    QPtrList<QuickDispatchObjectFactory> res;
    for ( QuickDispatchObjectFactory *f = dispatchObjFactories->first(); f; f = dispatchObjFactories->next() ) {
	QStringList l = f->featureList();
	if ( l.find( feature ) != l.end() )
	    res.append( f );
    }
    factoryCache.insert( feature, res );
    return res;
}

QSObject QuickInterpreter::object( const QString &name ) const
{
    QSObject g = env()->globalObject();
    Global *global = (Global*)&g; // ### ugly
    QSObject obj;
    if ( name.isEmpty() ) {
	obj = g;
    } else {
	int p = name.findRev( '.' );
	if ( p == -1 )
	    obj = global->get( name );
	else
	    obj = global->qualifiedGet( name.left( p ), name.mid( p + 1 ) );
    }
    return obj;
}

const QSClass *QuickInterpreter::classOf( const QSObject &obj ) const
{
    return obj.isA( env()->typeClass() ) ? obj.classValue() : obj.objectType();
}

QStringList QuickInterpreter::functionsOf( QSObject &obj, bool includeSignature, bool includeNative ) const
{
    QSMemberMap mmap = classOf( obj )->members( &obj );
    QMap<int, QString> functions;
    for ( QSMemberMap::Iterator it = mmap.begin(); it != mmap.end(); ++it ) {
	if ( ( (*it).type() == QSMember::ScriptFunction ||
	       includeNative && ( (*it).type() == QSMember::NativeFunction ||
				  (*it).type() == QSMember::NativeMemberFunction ) )
	     && !(*it).isPrivate() ) {
	    QString func = (*it).name();
	    // ### will break with mix of script and C++ functions
	    int idx = (*it).type() == QSMember::ScriptFunction ?
		      (*it).scriptFunction->index() : functions.size();
	    if ( includeSignature ) {
		if ( (*it).type() == QSMember::NativeFunction ||
		     (*it).type() == QSMember::NativeMemberFunction ) {
		    func += "()";
		} else {
		    QSFunctionScopeClass *fsc = (*it).scriptFunction->scopeDefinition();
		    int args = fsc->numberOfArguments();
		    func += "(";
		    if ( args > 0 ) {
			func += " ";
			QSMemberMap *members = fsc->definedMembers();
			QValueVector<QString> vec( args );
			for ( QSMemberMap::ConstIterator ait = members->begin();
			      ait != members->end(); ++ait ) {
			    if ( (*ait).index() < args )
				vec[ (*ait).index() ] = (*ait).name();
			}
			for ( int i = 0; i < args; ++i ) {
			    if ( i > 0 )
				func += ", ";
			    func += vec[i];
			}
			func += " ";
		    }
		    func += ")";
		}
	    }
	    functions[idx] = func;
	}
    }

    // assemble to list sorted by original index
    QStringList lst;
    QMap<int, QString>::const_iterator cit = functions.begin();
    for ( ; cit != functions.end(); ++cit )
	    lst << *cit;

    return lst;
}

QStringList QuickInterpreter::classesOf( QSObject &obj ) const
{
    const QSClass *cls = classOf( obj );
    QStringList lst;
    for ( int i = 0; i < cls->numberOfStaticVariables(); ++i ) {
	QSObject o = cls->staticMember( i );
	if ( o.isA( env()->typeClass() ) && o.classValue()->asClass() )
	    lst << o.classValue()->identifier();
    }
    return lst;
}

QStringList QuickInterpreter::variablesOf( QSObject &obj, bool includeStatic, bool includeCustom ) const
{
    const QSClass *cls = classOf( obj );
    QSMemberMap mmap = cls->members( &obj );
    QStringList lst;
    for ( QSMemberMap::Iterator it = mmap.begin(); it != mmap.end(); ++it ) {
	if ( ( (*it).type() == QSMember::Variable ||
	       includeCustom && (*it).type() == QSMember::Custom ) &&
	     (!(*it).isStatic() || includeStatic) &&
	     !(*it).isExecutable() )
	    lst << (*it).name();
    }
    return lst;
}
