#include <qmetaobject.h>
#include <private/qucom_p.h>

#include <qpen.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qvariant.h>

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

#include "global.h"
#include "jsbinding.h"
#include "jsobjectproxy.h"
#include "jsfactory.h"
#include "jsvalueproxy.h"
#include "jsopaqueproxy.h"
#include "kjsembedpart.h"

#ifndef QT_ONLY
#include "bindings/pen_imp.h"
#include "bindings/image_imp.h"
#include "bindings/pixmap_imp.h"
#include "bindings/painter_imp.h"
#include "bindings/brush_imp.h"
#endif // QT_ONLY

namespace KJSEmbed {

//
//
// Supported
// =========
//
// Bool, Double, CString, String, StringList, Int, UInt, Date, Time, DateTime,
// Rect, Size, Point, Pixmap, Image, Brush, Pen
//
// Opqaue
// ======
//
// Invalid, Map, List, Palette, ColorGroup,
// IconSet, PointArray, Region, Bitmap, Cursor, SizePolicy,
// ByteArray, BitArray 
//

KJS::Value convertToValue( KJS::ExecState *exec, const QVariant &val )
{
    if ( !val.isValid() )
	return KJS::Undefined();

    KJS::List items;
    uint type = val.type();

    if ( type == QVariant::String || type == QVariant::CString )
	return KJS::String( val.toString() );
    else if ( type == QVariant::Int )
	return KJS::Number( val.toInt() );
    else if ( type == QVariant::UInt )
	return KJS::Number( val.toUInt() );
    else if ( type == QVariant::Double )
	return KJS::Number( val.toDouble() );
    else if ( type == QVariant::Bool )
	return KJS::Boolean( val.toBool() );
    else if ( type == QVariant::Date || type == QVariant::Time || val == QVariant::DateTime
           || type == QVariant::Color || type == QVariant::KeySequence || type == QVariant::Font )
	return KJS::String( val.toString() );
    else if ( type == QVariant::Rect ) {
	QRect r( val.toRect() );
	items.append( KJS::Number( r.x() ) );
	items.append( KJS::Number( r.y() ) );
	items.append( KJS::Number( r.width() ) );
	items.append( KJS::Number( r.height() ) );
	return KJS::Object( exec->interpreter()->builtinArray().construct( exec, items ) );
    }
    else if ( type == QVariant::Point ) {
	QPoint p( val.toPoint() );
	items.append( KJS::Number( p.x() ) );
	items.append( KJS::Number( p.y() ) );
	return KJS::Object( exec->interpreter()->builtinArray().construct( exec, items ) );
    }
    else if ( type == QVariant::StringList ) {
    	QStringList lst = val.toStringList();
	for( uint idx = 0; idx < lst.count(); ++idx )
	    items.append( KJS::String( lst[idx] ) );
	return KJS::Object( exec->interpreter()->builtinArray().construct( exec, items ) );
    }
   /* else if ( type == QVariant::Size ) {
	QSize sz( val.toSize() );
	items.append( KJS::Number( sz.width() ) );
	items.append( KJS::Number( sz.height() ) );
	return KJS::Object( exec->interpreter()->builtinArray().construct( exec, items ) );
    }*/
    else if ( type == QVariant::Pixmap ) {
    	JSValueProxy *prx = new JSValueProxy( );
	prx->setValue(val);
	KJS::Object proxyObj(prx);
#ifndef QT_ONLY
    	Bindings::Pixmap::addBindings( exec, proxyObj);
#endif // QT_ONLY
	return proxyObj;
    }
    else if ( type == QVariant::Brush ) {
    	JSValueProxy *prx = new JSValueProxy( );
	prx->setValue(val);
	KJS::Object proxyObj(prx);
#ifndef QT_ONLY
    	Bindings::BrushImp::addBindings( exec, proxyObj);
#endif // QT_ONLY
	return proxyObj;
    }
    else if ( type == QVariant::Image ) {
    	JSValueProxy *prx = new JSValueProxy( );
	prx->setValue(val);
	KJS::Object proxyObj(prx);
#ifndef QT_ONLY
    	Bindings::ImageImp::addBindings( exec, proxyObj);
#endif // QT_ONLY
	return proxyObj;
    }
    else if ( type == QVariant::Pen ) {
    	JSValueProxy *prx = new JSValueProxy( );
	prx->setValue(val);
	KJS::Object proxyObj(prx);
#ifndef QT_ONLY
    	Bindings::Pen::addBindings( exec, proxyObj);
#endif // QT_ONLY
	return proxyObj;
    }
    else {
	kdDebug(80001) << "convertToValue() Creating value, type = " << val.typeName() << endl;
	
	JSValueProxy *valproxy = new JSValueProxy();
	KJS::Object obj = KJS::Object( valproxy );
	valproxy->addBindings( exec, obj );
	valproxy->setValue( val );
	
	return obj;
    }

    return KJS::Undefined();
}

QVariant convertToVariant( KJS::ExecState *exec, const KJS::Value &v )
{
    QVariant val;
    if ( v.isA( KJS::StringType ) )
	val = v.toString(exec).qstring();
    else if ( v.isA( KJS::NumberType ) )
	val = v.toInteger(exec);
    else if ( v.isA( KJS::BooleanType ) )
	val = v.toBoolean(exec);
    else if ( v.isA( KJS::ObjectType ) ) {
	KJS::Object obj = v.toObject(exec);
	kdDebug(80001) << "convertToVariant() got an " << obj.className().qstring() << endl;

	JSObjectProxy *prx = JSProxy::toObjectProxy( obj.imp() );
	JSValueProxy *vprx = dynamic_cast<JSValueProxy *>( obj.imp() );
	JSOpaqueProxy *oprx = dynamic_cast<JSOpaqueProxy *>( obj.imp() );

	if ( prx ) {
	    QObject *o = prx->object();
	    QCString cs( o ? o->className() : "" );
	    //kdDebug(80001) << "Converting JSObjectProxy, class " << cs << endl;

	    //if ( o && (cs == "KJSEmbed::Bindings::Pixmap") )
		//val = o->property( "pixmap" );
	    //if ( o && (cs == "KJSEmbed::Bindings::Pen") )
		//val = o->property( "pen" );

	    if ( obj.implementsConstruct() )
		val = QString("new %1();").arg( obj.className().qstring() );
	    else if ( obj.implementsCall() )
		val = QString("%1(...);").arg( obj.className().qstring() );
	}
	else if ( vprx ) {
	    kdDebug(80001) << "convertToVariant() got a variant JSValueProxy" << endl;
	    return vprx->toVariant();
	} else if ( oprx ) {
	    kdDebug(80001) << "convertToVariant() got a JSOpaqueProxy" << endl;
	}
    }
    else {
	QString s( "Unsupported value type, %1" );
	val = s.arg( (int) v.type() );
    }

    return val;
}

QStringList dumpCompletion( KJS::ExecState *exec, KJS::Object &obj)
{
	QStringList returnList;
	if ( !obj.isValid() )
	{
		return returnList;
	}

	KJS::ReferenceList l = obj.propList( exec );
	KJS::ReferenceListIterator propIt = l.begin();
	while ( propIt != l.end() ) {

		KJS::Identifier name = propIt->getPropertyName( exec );
		if ( obj.hasProperty( exec, name ) ) {
			KJS::Value v = obj.get( exec, name );
			KJS::Object vobj = v.toObject( exec );
			QString nm( name.ustring().qstring() );
			QString vl;

			if ( vobj.implementsConstruct() ) {
				// Do nothing
			}
			else if ( vobj.implementsCall() ) {
				returnList << nm;
			}
			else {
				QVariant val = convertToVariant( exec, v );
				vl = val.toString();
				returnList << nm;
			}
		}

		propIt++;
	}

	JSObjectProxy *proxy = JSProxy::toObjectProxy( obj.imp() );
	QObject *qo = proxy ? proxy->object() : 0;
	QMetaObject *mo = qo ? qo->metaObject() : 0;

	// QObject
	if ( proxy ) {

		// QProperties
		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 )
				continue;
			returnList << name;
		}

		// Slots
		KJS::List items;
		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 ) {
				returnList << nm;
			}
		}
	}
    return returnList;
}

QString dumpObject( KJS::ExecState *exec, KJS::Object &obj )
{
    if ( !obj.isValid() )
	return QString( "Invalid object\n" );

    QString properties = "";
    QString methods = "";

    KJS::ReferenceList l = obj.propList( exec );
    KJS::ReferenceListIterator propIt = l.begin();
    while ( propIt != l.end() ) {
	KJS::Identifier name = propIt->getPropertyName( exec );
	if ( obj.hasProperty( exec, name ) ) {
	    KJS::Value v = obj.get( exec, name );
	    KJS::Object vobj = v.toObject( exec );
	    QString nm( name.ustring().qstring() );
	    QString vl;
		//kdDebug() << "Getting: " << name.ustring().qstring() << " "
		// << vobj.prototype().toString(exec).qstring() << endl;
	    if ( vobj.implementsConstruct() ) {
		// Do nothing
		//kdDebug() << "implementsConstruct: " << nm << endl;
	    } else if ( vobj.implementsCall() ) {
		    //kdDebug() << "implementsCall: " << nm << endl;
		methods += QString( "<li><b>%1(...)</b>\n" ).arg(nm);
	    }
	    else {
		QVariant val = convertToVariant( exec, v );
		vl = val.toString();
		properties += QString( "<li><b>%1</b> %2\n" ).arg(nm).arg(vl);
	    }
	}

	propIt++;
    }

    QString s;

    // Methods
    s += QString("<h2>Methods</h2>\n");
    if ( !methods.isEmpty() ) {
	s += "<ul>";
	s += methods;
	s += "</ul>";
    }
    else
	s += "<i>None</i>\n";

    // JS properties
    s += QString("<h2>Properties</h2>\n");
    if ( !properties.isEmpty() ) {
	s += "<ul>";
	s += properties;
	s += "</ul>";
    }
    else
	s += "<i>None</i>\n";

    s += dumpQObject( exec, obj );
    return s;
}

QString dumpQObject( KJS::ExecState */*exec*/, KJS::Object &obj )
{
    if ( !obj.isValid() )
	return QString( "Invalid object\n" );

    QString s("");
    JSObjectProxy *proxy = JSProxy::toObjectProxy( obj.imp() );
    QObject *qo = proxy ? proxy->object() : 0;
    QMetaObject *mo = qo ? qo->metaObject() : 0;

    // QObject
    if ( proxy ) {

	// QProperties
	s += QString("<h2>Qt Properties</h2>\n");
	s += "<ul>";

	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 )
		continue;

	    QVariant vl = qo->property( name.data() );
	    s += QString( "<li><b>%1 %2</b> %3\n" ).arg(vl.typeName()).arg(name).arg(vl.toString());
	}

	s += "</ul>";
	if ( propList.isEmpty() )
	    s += "<i>None</i>";


	// Signals
	s += QString("<h2>Qt Signals</h2>\n");
	s += "<ul>";
	QStrList signalList( mo->signalNames( true ) );

	for ( QStrListIterator iter(signalList); iter.current(); ++iter ) {

	    QCString name = iter.current();
	    QString nm( name );

	    int signalid = mo->findSignal( name.data(), true );
	    if ( signalid != -1 )
		s += QString("<li><b>%1</b>\n").arg(nm);
	}

	s += "</ul>";
	if ( signalList.isEmpty() )
	    s += "<i>None</i>";

	// Slots
	s += QString("<h2>Qt Slots</h2>\n");
	s += "<ul>";

	KJS::List items;
	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 ) {
		const QMetaData *md = mo->slot( slotid, true );
		const QUMethod *method = md->method;
		if ( method->count && (method->parameters->inOut == QUParameter::Out) ) {
		    QCString typenm( method->parameters->type->desc() );
		    if ( typenm == "ptr" ) {
			s += QString("<li><b>%1 %2</b>\n")
			       .arg((const char *) method->parameters->typeExtra).arg(nm);
		    }
		    else {
			s += QString("<li><b>%1 %2</b>\n")
			       .arg(typenm.data()).arg(nm);
		    }
		}
		else
		    s += QString("<li><b>void %1</b>\n").arg(nm);
	    }
	}

	s += "</ul>";
	if ( slotList.isEmpty() )
	    s += "<i>None</i>";
    }

    return s;
}



QPen extractQPen(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toPen() : QPen();
}
QBrush extractQBrush(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toBrush() : QBrush();
}

QFont extractQFont(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toFont() : QFont();
}

QPixmap extractQPixmap( KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toPixmap() : QPixmap();
}

QImage extractQImage( KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toImage() : QImage();
}

QString extractQString( KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? args[idx].toString(exec).qstring() : QString::null;
}

int extractInt( KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? args[idx].toInteger(exec) : 0;
}

QColor extractColor(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toColor() : QColor();
}

QSize extractSize(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? convertToVariant(exec, args[idx]).toSize() : QSize();
}

bool extractBool(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? args[idx].toBoolean(exec) : false;
}

double extractDouble(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? args[idx].toNumber(exec) : 0.0;
}

uint extractUInt(KJS::ExecState *exec, const KJS::List &args, int idx)
{
     return (args.size() > idx) ? args[idx].toUInt32(exec) : 0;
}

} // namespace KJSEmbed
