/****************************************************************************
** $Id: qsengine.cpp  beta1   edited Dec 16 17:28 $
**
** 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 "qsengine.h"
#include "qstypes.h"
#include "qsinternal.h"
#include "qsenv.h"
#include "qslexer.h"
#include "qsnodes.h"
#include "qsfuncref.h"

QSEngine::QSEngine( QObject *parent, const char *name )
    : QObject( parent, name )
{
    rep = new QSEngineImp( this );
    rep->init();
}

QSEngine::~QSEngine()
{
    delete rep;

#ifdef QSDEBUG_MEM
    printf("Imp::count: %d\n", Imp::count);
    //  assert(Imp::count == 0);
#endif
}

void QSEngine::init()
{
    rep->init();
}

QSEngine *QSEngine::current()
{
    return QSEngineImp::current() ? QSEngineImp::current()->scr : 0L;
}

int QSEngine::recursion() const
{
    return rep->recursion;
}

bool QSEngine::evaluate(const char *code)
{
    return rep->evaluate( QString( code ) );
}

bool QSEngine::evaluate(const QString &code)
{
    return rep->evaluate( code );
}

bool QSEngine::evaluate( const QSObject &thisV, const QString &code, int linezero )
{
    return rep->evaluate( code, thisV.isValid() ? &thisV : 0, linezero );
}

bool QSEngine::call( const QString &func, const QSList &args )
{
    return rep->call(0, func, args);
}

bool QSEngine::call( QSObject *scope, const QString &func,
		     const QSList &args )
{
    return rep->call( scope, func, args);
}

void QSEngine::clear()
{
    rep->clear();
}

QSObject QSEngine::returnValue() const
{
    return rep->retVal;
}

int QSEngine::errorType() const
{
    return rep->errType;
}

QValueList<uint> QSEngine::errorLines() const
{
    return rep->errLines;
}

QStringList QSEngine::errorMessages() const
{
    return rep->errMsgs;
}

extern int qsyyparse();

bool QSEngine::checkSyntax( const QString &code, int /*checkMode*/,
			    bool deleteNodes )
{
    Q_ASSERT( QSLexer::curr() );

    rep->errType = 0;
    rep->errLines.clear();
    rep->errMsgs.clear();

    QSLexer::curr()->setCode( code );

    QSNode::setFirstNode( 0 );
    int parseError = qsyyparse();

    if ( deleteNodes )
	QSNode::deleteAllNodes();

    if ( parseError ) {
	rep->errType = 99; /* TODO */
	int l = QSLexer::curr()->lineNo();
	rep->errLines.append( l );
	rep->errMsgs.append( "Parse error at line " + QSString::from( l ) );
	/* TODO: either clear everything or keep previously
	   parsed function definitions */
	//    QSNode::deleteAllNodes();
	return FALSE;
    }

    return TRUE;
}

void qsengine_dumptype( const QSClass *cl )
{
    printf( "class %s", cl->identifier().latin1() );
    printf( "  - %s\n", cl->isExecutable() ? "executable" : "not executable" );
    printf( "  - %s\n", cl->isFinal() ? "final" : "not final" );
    QSMemberMap::Iterator it = cl->definedMembers()->begin();
    for( ; it!=cl->definedMembers()->end(); it++ ) {
	QSMember mem = *it;
	QString line = QString("  ") + mem;
	printf( "%s\n", line.latin1() );
    }
    if( cl->enclosingClass() )
	qsengine_dumptype( cl->enclosingClass() );
    if( cl->base() )
	qsengine_dumptype( cl->base() );
}


static QSObject dumpType( const QSList &args )
{
    if ( args.size()>=1 &&
	 args[0].objectType()==args[0].objectType()->env()->typeClass() ) {
	printf( "DUMP TYPE::\n" );
	QSClass *cl = args[0].classValue();
	qsengine_dumptype( cl );
    }
    printf( "\n" );
    return QSUndefined();
}

static QSObject dumpObject( const QSList &args )
{
    if( args.size()>=1 ) {
	QSObject obj = args[0];
	const QSClass * cl = obj.objectType();
	printf( "DUMP OBJECT:: %p\n", obj.shVal() );
	printf( "class %s :: %s\n",
		cl->name().latin1(),
		cl->identifier().latin1() );
 	QSMemberMap::Iterator it = cl->definedMembers()->begin();
	for( ; it!=cl->definedMembers()->end(); it++ ) {
	    QSMember mem = *it;
	    if( mem.isReadable() ) {
		QSObject value = cl->fetchValue( &obj, mem );
		if( mem.type()==QSMember::Variable )
		    printf( "  %2d: %s = %s\n", mem.index(), mem.name().latin1(), value.debugString().latin1() );
		else
		    printf( "      %s = %s\n", mem.name().latin1(), value.debugString().latin1() );
	    }
	}
    }
    printf( "\n" );
    return QSUndefined();
}

static QSObject dumpScope( const QSList & /*args*/ )
{
    ScopeChain chain = QSEnv::current()->scope();
    ScopeChain::ConstIterator it = chain.begin();
    printf( "\n---------- DUMP SCOPE ---------- \n\n" );
    while( it!=chain.end() ) {
	dumpObject( QSList( *it ) );
	if( (*it).objectType() == QSEnv::current()->typeClass() ) {
	    dumpType( *it );
	}
	it++;
    }
    printf( "---------- DUMP COMPLETE ---------- \n" );
    return QSUndefined();
}



static QSObject debugFunction( const QSList &args )
{
    qDebug( "---> " + args[0].toString() );
    return QSUndefined();
}

static QSObject printFunction( const QSList &args )
{
    qDebug( args.size()>0 ? args[0].toString() : QString() );
    return QSUndefined();
}

void QSEngine::enableDebug()
{
    rep->init();

    QSMember debug( &debugFunction );
    env()->globalClass()->addMember( "debug", debug );

    QSMember dump( &dumpType );
    env()->globalClass()->addMember( "dumpType", dump );

    QSMember dumpO( &dumpObject );
    env()->globalClass()->addMember( "dumpObject", dumpO );

    QSMember dumpS( &dumpScope );
    env()->globalClass()->addMember( "dumpScope", dumpS );

    QSMember print( &printFunction );
    env()->globalClass()->addMember( "print", print );
}

/*!
  \internal
*/

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

/*!
  \internal
*/

void QSEngine::requestPackage( const QString &package, QString &err )
{
    emit packageRequest( package, err );
}

QSEnv *QSEngine::env() const
{
    return rep->env();
}

/*!
  Register the type described by \a c with the engine. Ownership of the
  type object is passed on. The interpreter will delete all registered
  types on destruction or a call to clear().

  \sa type()
*/

void QSEngine::registerType( QSClass *c )
{
    QSClass * glob = env()->globalClass();
    QSObject tobj( env()->typeClass() );
    tobj.setValue( c );
    glob->addStaticVariableMember( c->identifier(), tobj,
				   AttributeExecutable );
}

