/*
 *  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 "factory.h"
#include "jsobjectproxy_imp.h"

// Declare the 
// extern Q_EXPORT QUType_QVariant static_QUType_QVariant;

namespace KJSEmbed {
namespace Bindings {

JSObjectProxyImp::JSObjectProxyImp( int mid, const JSObjectProxy *parent )
    : KJS::ObjectImp(), id(mid), proxy(parent), obj(parent->obj)
{
}

JSObjectProxyImp::JSObjectProxyImp( int mid, const QCString &name, const JSObjectProxy *parent )
    : KJS::ObjectImp(), id(mid), slotname(name), proxy(parent), obj(parent->obj)
{
}

KJS::Value JSObjectProxyImp::childAt( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &args )
{
    const QObjectList *kids = obj->children();
    if ( !kids )
	return KJS::Null();
    
    QObjectList l( *kids );
    uint i = args[0].toUInt32( state );

    if ( i >= l.count()  )
	return KJS::Null();

    QObject *child = l.at( i );
    if ( child && proxy->securityPolicy()->isObjectAllowed( proxy, child ) )
	return proxy->createSubProxy( child, state );
    
    return KJS::Null();
}

KJS::Value JSObjectProxyImp::findChild( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &args )
{
    QString s = args[0].toString( state ).qstring();
    QObject *child = obj->child( s.ascii() );

    if ( child && proxy->securityPolicy()->isObjectAllowed( proxy, child ) )
	return proxy->createSubProxy( child, state );
    
    return KJS::Null();
}

KJS::Value JSObjectProxyImp::children( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &/*args*/ )
{
    KJS::List items;
    const QObjectList *kids = obj->children();
    if ( kids ) {
        QObjectList l( *kids );

	for ( uint i = 0 ; i < l.count() ; i++ ) {
	    QObject *child = l.at( i );
	    QCString nm = ( child ? child->name() : "<null>" );
	    items.append( KJS::String( QString(nm) ) );
	}
    }

    return KJS::Object( proxy->js->builtinArray().construct( state, items ) );
}

KJS::Value JSObjectProxyImp::properties( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &/*args*/ )
{
    KJS::List items;
    QMetaObject *mo = obj->metaObject();
    QStrList propList( mo->propertyNames( true ) );
    
    for ( QStrListIterator iter(propList); iter.current(); ++iter ) {
	
	QCString name = iter.current();
	int propid = mo->findProperty( name.data(), true );
	if ( propid != -1 ) {
	    items.append( KJS::String( QString(name) ) );
	}
    }
    
    return KJS::Object( proxy->js->builtinArray().construct( state, items ) );
}

KJS::Value JSObjectProxyImp::slotz( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &/*args*/ )
{
    KJS::List items;
    QMetaObject *mo = obj->metaObject();
    QStrList slotList( mo->slotNames( true ) );
    
    for ( QStrListIterator iter(slotList); iter.current(); ++iter ) {
	
	QCString name = iter.current();
	QString nm( name );

	int slotid = mo->findSlot( name.data(), true );
	if ( (slotid != -1) && (mo->slot( slotid, true )->access == QMetaData::Public) ) {
	    if ( findSignature(nm) >= 0 ) {
		items.append( KJS::String(nm) );
	    } else {
		kdDebug() << "slotz: Reject " << name << endl;
	    }
	}
    }
    
    return KJS::Object( proxy->js->builtinArray().construct( state, items ) );
}

int JSObjectProxyImp::findSignature( const QString &sig )
{
    if ( sig.contains("()") )
	return MethodSlot;
    else if ( sig.contains( QRegExp("\\(int\\)") ) )
	return MethodSlotInt;
    else if ( sig.contains( QRegExp("\\(bool\\)") ) )
	return MethodSlotBool;
    else if ( sig.contains( QRegExp("\\(const QString&\\)") ) )
	return MethodSlotString;
    else if ( sig.contains( QRegExp("\\(double\\)") ) )
	return MethodSlotDouble;
    else if ( sig.contains( QRegExp("\\(int,int\\)") ) )
	return MethodSlotIntInt;
    else if ( sig.contains( QRegExp("\\(const QString&,int\\)") ) )
	return MethodSlotStringInt;

    kdDebug() << "findSignature: Failed " << sig << endl;
    return -1;
}

KJS::Value JSObjectProxyImp::callSlot( KJS::ExecState *state, KJS::Object &/*self*/, const KJS::List &args )
{
    int slotid = obj->metaObject()->findSlot( slotname.data(), true );
    if ( slotid == -1 )
	return KJS::Null();

    QUObject uo[ 3 ] = { QUObject(), QUObject(), QUObject() };

    switch (id) {
	case MethodSlot:
	    break;
	case MethodSlotInt:
	    static_QUType_int.set( uo+1, args[0].toInteger( state ) );
	    break;
	case MethodSlotBool:
	    static_QUType_bool.set( uo+1, args[0].toBoolean( state ) );
	    break;
	case MethodSlotString:
	    static_QUType_QString.set( uo+1, args[0].toString(state).qstring() );
	    break;
	case MethodSlotDouble:
	    static_QUType_double.set( uo+1, args[0].toNumber( state ) );
	    break;
	case MethodSlotIntInt:
	    static_QUType_int.set( uo+1, args[0].toInteger( state ) );
	    static_QUType_int.set( uo+2, args[1].toInteger( state ) );
	    break;
	case MethodSlotStringInt:
	    static_QUType_QString.set( uo+1, args[0].toString(state).qstring() );
	    static_QUType_int.set( uo+2, args[1].toInteger( state ) );
	    break;
	default:
	    return KJS::Null();
    }

    obj->qt_invoke( slotid, uo );

#if 0
    // Testing code for return values
    QVariant val;
    val = static_QUType_QVariant.get( uo+1 );
    if ( val.isValid() ) {
	kdDebug() << "Variant value is " << val.toString() << endl;
	return convertToValue( val );
    }
    else {
	kdDebug() << "Variant value is invalid" << endl;
    }
#endif

    return KJS::Null();
}

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


    switch( id ) {
	case MethodParent:
	{
	    QObject *po = obj->parent();
	    if ( po && proxy->securityPolicy()->isObjectAllowed( proxy, po ) )
		return proxy->createSubProxy( po, state );

	    return KJS::Null();
	}
	break;
	case MethodChildCount:
	{
	    const QObjectList *kids = obj->children();
	    return kids ? KJS::Number( kids->count() )	: KJS::Number(0);
	}
	break;
	case MethodChildAt:
	case MethodFindChild:
	case MethodChild:
	{
	    if ( args[0].isA( KJS::NumberType ) )
		return childAt( state, self, args );
	    else
		return findChild( state, self, args );
	}
	break;
	case MethodCreate:
	{
	    QString cl = args[0].toString( state ).qstring();
	    QString nm = args[1].toString( state ).qstring();

	    const JSSecurityPolicy *sp = proxy->securityPolicy();
	    if ( !sp->isCreateAllowed(proxy, obj, nm, cl) ) {
		kdWarning() << "JSObjectProxy: Denied by security policy" << endl;
		return KJS::Null();
	    }

	    QObject *child = JSFactory::create( cl.latin1(), proxy->obj, nm.latin1() );
	    if ( !child )
		return KJS::Null();

	    return proxy->createSubProxy( child, state );
	}
	break;
	case MethodChildren:
	    return children( state, self, args );
	    break;
	case MethodProps:
	    return properties( state, self, args );
	    break;
	case MethodSlots:
	    return slotz( state, self, args );
	    break;
	case MethodSlot:
	case MethodSlotInt:
	case MethodSlotBool:
	case MethodSlotString:
	case MethodSlotDouble:
	case MethodSlotIntInt:
	case MethodSlotStringInt:
	    return callSlot( state, self, args );
	    break;
	default:
	    break;
    }

    return KJS::ObjectImp::call( state, self, args );
}

} // namespace KJSEmbed::Bindings
}; // namespace KJSEmbed

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

