/*
 *  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 <qobjectlist.h>
#include <qdialog.h>
#include <qmetaobject.h>
#include <qregexp.h>
#include <qsignal.h>
#include <qstrlist.h>
#include <qtimer.h>
#include <qvariant.h>

#include <private/qucomextra_p.h>

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

#include "kjsembedpart.h"
#include "securitypolicy.h"

#include "jsobjectproxy.h"
#include "jsobjectproxy_imp.h"

// Declare the 
// extern Q_EXPORT QUType_QVariant static_QUType_QVariant;

namespace KJSEmbed {

//class JSObjectProxyPrivate {};

JSObjectProxy::JSObjectProxy( KJS::Interpreter *jsi, QObject *target,
			      QObject *r, const JSSecurityPolicy *sp )
    : KJS::ObjectImp(), js(jsi), obj(target), root(r)
{
    policy = sp ? sp : JSSecurityPolicy::defaultPolicy();
}

JSObjectProxy::JSObjectProxy( KJS::Interpreter *jsi, QObject *target, QObject *r )
    : KJS::ObjectImp(), js(jsi), obj(target), root(r)
{
    policy = JSSecurityPolicy::defaultPolicy();
}

JSObjectProxy::JSObjectProxy( KJS::Interpreter *jsi, QObject *target )
    : KJS::ObjectImp(), js(jsi), obj(target), root(target)
{
    policy = JSSecurityPolicy::defaultPolicy();
}

void JSObjectProxy::setSecurityPolicy( const JSSecurityPolicy *pol )
{
    policy = pol ? pol : new JSSecurityPolicy();
}

bool JSObjectProxy::isAllowed( KJS::Interpreter *js ) const
{
    return policy->isInterpreterAllowed( this, js );
}

KJS::UString JSObjectProxy::toString( KJS::ExecState *state ) const
{
    if ( !state ) {
	kdWarning() << "JS toString with null state, ignoring" << endl;
	return KJS::UString();
    }
    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::Object JSObjectProxy::createSubProxy( QObject *target, KJS::ExecState *state ) const
{
//    kdDebug() << "JSObjectProxy::createSubProxy: For target '" << target->name() << "'" << endl;
    JSObjectProxy *prx = new JSObjectProxy( js, target, root, policy );

    KJS::Object proxyObj( prx );
    prx->addBindings( state ? state : js->globalExec(), proxyObj );

    return proxyObj;
}

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::Null();
    }

    if ( !policy->isPropertyAllowed(this, obj, p.ascii()) )
	return KJS::ObjectImp::get( state, p );

    if ( !obj ) {
	kdDebug() << "JS getting '" << p.qstring() << "' but qobj has died" << endl;
	return KJS::ObjectImp::get( state, p );
    }

    // Properties
    QString prop = p.qstring();
    QMetaObject *meta = obj->metaObject();
    
    if ( meta->findProperty( p.ascii(), true ) != -1 ) {
	QVariant val = obj->property( prop.ascii() );
	return convertToValue( val );
    }

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

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

    if ( !policy->hasCapability( JSSecurityPolicy::CapabilitySetProperties ) ) {
	KJS::ObjectImp::put( state, p, v, attr );
	return;
    }
    
    if ( !obj ) {
	kdDebug() << "JS setting '" << p.ascii() << "' but qobj has died" << endl;
	KJS::ObjectImp::put( state, p, v, attr );
	return;
    }
    
    QMetaObject *meta = obj->metaObject();
    
    if ( meta->findProperty( p.ascii(), true ) != -1 ) {
	QVariant val = convertToVariant( state, v );

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

//
// Implementation of JS Method Bindings
//

void JSObjectProxy::addBindings( KJS::ExecState *state, KJS::Object &object )
{
//    kdDebug() << "JSObjectProxy::addBindings() " << (obj->name() ? obj->name() : "dunno")
//	      << ", class " << obj->className() << endl;

    if ( policy->hasCapability( JSSecurityPolicy::CapabilityGetProperties|JSSecurityPolicy::CapabilitySetProperties ) ) {
	object.put( state, "properties", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodProps, this)) );
    }

    if ( policy->hasCapability( JSSecurityPolicy::CapabilityFactory ) ) {
	object.put( state, "create", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodCreate, this)) );
    }

    if ( policy->hasCapability( JSSecurityPolicy::CapabilityTree ) ) {

	object.put( state, "parent", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodParent, this)) );
	object.put( state, "childCount", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodChildCount, this)) );
	object.put( state, "childAt", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodChildAt, this)) );
	object.put( state, "findChild", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodFindChild, this)) );
	object.put( state, "child", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodChild, this)) );
	object.put( state, "children", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodChildren, this)) );
    }

    if ( policy->hasCapability( JSSecurityPolicy::CapabilitySlots ) ) {
	// Get meta data
	QMetaObject *mo = obj->metaObject();
	QStrList slotList( mo->slotNames( true ) );

	int count = 0;
	for ( QStrListIterator iter(slotList); iter.current(); ++iter ) {

	    QCString name = iter.current();
	    QCString jsname = name;
	    jsname.detach();
	    jsname.replace( QRegExp("\\([^\\)]*\\)"), "" );

	    int slotid = mo->findSlot( name.data(), true );
	    if ( (slotid != -1) && (mo->slot( slotid, true )->access == QMetaData::Public) ) {

		int id = Bindings::JSObjectProxyImp::findSignature( name );
		if ( id >= 0 ) {
		    Bindings::JSObjectProxyImp *imp = new Bindings::JSObjectProxyImp( id, name, this);
		    object.put( state, jsname.data(), KJS::Object(imp) );
		    count++;
		}
	    }
	}

//    kdDebug() << "Published " << count << " slots" << endl;
	object.put( state, "slots", KJS::Object(new Bindings::JSObjectProxyImp(Bindings::JSObjectProxyImp::MethodSlots, this)) );
    }
}

}; // namespace KJSEmbed

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

