/*
 *  Copyright (C) 2001-2002, Richard J. Moore <rich@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 */

#include <qobject.h>
#include <qptrlist.h>
#include <qobjectlist.h>
#include <qmetaobject.h>
#include <qvariant.h>

#include <kdebug.h>
#include <kjs/interpreter.h>
#include <kjs/ustring.h>

#include "jsobjectproxy.h"

namespace KJSEmbed {

JSObjectProxy::JSObjectProxy( KJS::Interpreter *jsi, QObject *target, QObject *r )
    : KJS::ObjectImp(), js(jsi), obj(target), root(r)
{
}

JSObjectProxy::JSObjectProxy( KJS::Interpreter *jsi, QObject *target )
    : KJS::ObjectImp(), js(jsi), obj(target), root(target)
{
}

KJS::UString JSObjectProxy::toString( KJS::ExecState *state ) const
{
    if ( !isAllowed( state->interpreter() ) ) {
	kdWarning() << "JS toString request from unknown interpreter, ignoring" << endl;
	return KJS::UString();
    }

    QString s( "%1 (%2)" );
    s = s.arg( obj ? obj->name() : "Dead Object" );
    s = s.arg( obj ? obj->className() : "" );
    return KJS::UString(s);
}

KJS::Value JSObjectProxy::get( KJS::ExecState *state, const KJS::UString &p ) const
{
    if ( !isAllowed( state->interpreter() ) ) {
	kdWarning() << "JS get request from unknown interpreter, ignoring" << endl;
	return KJS::Undefined();
    }

    if ( !obj ) {
	kdDebug() << "JS getting '" << p.ascii() << "' but qobj has died" << endl;
	return KJS::Undefined();
    }

    // Properties
    QString prop = p.qstring();
    QVariant val = obj->property( prop.ascii() );
    if ( val.isValid() ) {
	if ( (val.type() == QVariant::String) || (val.type() == QVariant::CString) )
	    return KJS::String( val.toString() );
	else if ( val.type() == QVariant::Int )
	    return KJS::Number( val.toInt() );
	else if ( val.type() == QVariant::Double ) {
	    return KJS::Number( val.toDouble() );
	}
    }

    return KJS::ObjectImp::get( state, p );
}

void JSObjectProxy::put( KJS::ExecState *state,
			 const KJS::UString &p, const KJS::Value &v,
			 int attr )
{
    kdDebug() << "JS put request for property '" << p.qstring() << "'" << endl;

    if ( !isAllowed( state->interpreter() ) ) {
	kdWarning() << "JS put request from unknown interpreter, ignoring" << endl;
	return;
    }

    if ( !obj ) {
	kdDebug() << "JS setting '" << p.ascii() << "' but qobj has died" << endl;
	return;
    }

    QMetaObject *meta = obj->metaObject();

    if ( meta->findProperty( p.ascii(), true ) != -1 ) {
	QVariant val;

	if ( v.isA( KJS::StringType ) )
	    val = v.toString( state ).qstring();
	else if ( v.isA( KJS::NumberType ) )
	    val = v.toInteger( state );
	else if ( v.isA( KJS::BooleanType ) )
	    val = v.toBoolean( state );

	kdDebug() << "JS setting '" << p.ascii() << "' ( " << val.typeName() << ") to '"
		  << val.asString() << "'" << endl;
	bool ok = obj->setProperty( p.ascii(), val );
	kdDebug() << "setProperty returned " << ok << endl;
    }
    else {
	KJS::ObjectImp::put( state, p, v, attr );
    }
}

//
// Security
//

bool JSObjectProxy::isAllowed( KJS::Interpreter *i ) const
{
    if ( !js )
	return true;
    else if ( js == i )
	return true;

    return false;
}

bool JSObjectProxy::isAllowed( QObject *obj ) const
{
    if ( !obj )
	return false;

    do {
	if ( obj == root )
	    return true;
    } while( (obj = obj->parent()) );

    return false;
}

bool JSObjectProxy::isAllowed( QObject *obj, const char */*name*/ ) const
{
    return isAllowed( obj );
}

//
// Implementation of JS Method Bindings
//
void JSObjectProxy::addBindings( KJS::ExecState *state, KJS::Object &object )
{
    object.put( state ,"parent", KJS::Object( new MethodImp( MethodImp::MethodParent, this ) ) );
    object.put( state ,"childCount", KJS::Object( new MethodImp( MethodImp::MethodChildCount, this ) ) );
    object.put( state ,"childAt", KJS::Object( new MethodImp( MethodImp::MethodChildAt, this ) ) );
    object.put( state ,"findChild", KJS::Object( new MethodImp( MethodImp::MethodFindChild, this ) ) );
}

JSObjectProxy::MethodImp::MethodImp( MethodId mid, const JSObjectProxy *parent )
    : KJS::ObjectImp(), id(mid), proxy(parent)
{
}

KJS::Value JSObjectProxy::MethodImp::call( KJS::ExecState *state, KJS::Object &/*self*/,
					   const KJS::List &args )
{
    if ( state->interpreter() != proxy->jscript() ) {
	kdWarning() << "JSObjectProxy::Method call from unknown interpreter!" << endl;
	return KJS::Undefined();
    }

    kdDebug() << "JSObjectProxy::Method call " << (int) id << endl;

    QObject *obj = proxy->obj;
    KJS::Interpreter *js = proxy->jscript();
    QObjectList l( *(obj->children()) );

    switch( id ) {
    case MethodParent:
	{
	    QObject *child = proxy->obj->parent();
	    if ( child && proxy->isAllowed( child ) ) {
		    JSObjectProxy *prx = new JSObjectProxy( js, child, proxy->root );
		    KJS::Object proxyObj( prx );
		    prx->addBindings( state, proxyObj );
		    return KJS::Object( prx );
	    }
	}
	break;
    case MethodChildCount:
	{
	    return KJS::Number( l.count() );
	}
	break;
    case MethodChildAt:
	{
	    KJS::Value v = args[0];
	    uint i = v.toUInt32( state );
	    if ( i < l.count()  ) {
		QObject *child = l.at(v.toUInt32( state ));
		if ( proxy->isAllowed( child ) ) {
		    JSObjectProxy *prx = new JSObjectProxy( js, child, proxy->root );
		    KJS::Object proxyObj( prx );
		    prx->addBindings( state, proxyObj );
		    return KJS::Object( prx );
		}
	    }
	}
	break;
    case MethodFindChild:
	{
	    QString s = args[0].toString( state ).qstring();

	    QObject *child = proxy->obj->child( s.ascii() );
	    if ( child && proxy->isAllowed( child ) ) {
		    JSObjectProxy *prx = new JSObjectProxy( js, child, proxy->root );
		    KJS::Object proxyObj( prx );
		    prx->addBindings( state, proxyObj );
		    return KJS::Object( prx );
	    }
	}
	break;
    default:
	break;	    
    };
    
    return KJS::Undefined();
}

}; // namespace KJSEmbed

// Local Variables:
// c-basic-offset: 4
// End:

