/*
 *  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 <stdio.h>

#include <qobject.h>
#include <qdialog.h>
#include <qfile.h>
#include <qtextstream.h>
#include <qwidget.h>
#include <qwidgetfactory.h>

#include <kaction.h>
#include <kdebug.h>

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

#include "jsbinding.h"
#include "jsobjectproxy.h"
#include "jsconsolewidget.h"

#include "kjsembedpart.h"
#include "kjsembedpart.moc"

//
// KPart Implementation
//
namespace KJSEmbed {

KJSEmbedPart::KJSEmbedPart( QObject *parent, const char *name )
    : KParts::ReadOnlyPart( parent, name ? name : "kjsembed_part" ),
	      KJSEmbed::XMLActionRunner(),
	      widgetparent(0), widgetname(name ? name : "kjsembed_part"),
	      jsConsole(0), deletejs(true)
{
    js = createInterpreter();
    global = js->globalObject();
    publishStdBindings();

    xmlclient = new KJSEmbed::XMLActionClient( this );
    xmlclient->setActionCollection( actionCollection() );
    xmlclient->setRunner( this );
}

KJSEmbedPart::KJSEmbedPart( QWidget *wparent, const char *wname, QObject *parent, const char *name )
    : KParts::ReadOnlyPart( parent, name ? name : (wname?wname:"jsembed_part") ),
	      widgetparent(wparent), widgetname(wname),
	      jsConsole(0), deletejs(true)
{
    js = createInterpreter();
    global = js->globalObject();
    publishStdBindings();

    xmlclient = new KJSEmbed::XMLActionClient( this );
    xmlclient->setActionCollection( actionCollection() );
    xmlclient->setRunner( this );
}

KJSEmbedPart::KJSEmbedPart( KJS::Interpreter *jsi, QWidget *wparent, const char *wname,
			  QObject *parent, const char *name )
    : KParts::ReadOnlyPart( parent, name ? name : (wname?wname:"jsembed_part") ),
	      widgetparent(wparent), widgetname(wname),
	      js(jsi), jsConsole(0), deletejs(false)
{
    if ( !js ) {
	js = createInterpreter();
	deletejs = true;
    }
    global = js->globalObject();
    publishStdBindings();

    xmlclient = new KJSEmbed::XMLActionClient( this );
    xmlclient->setActionCollection( actionCollection() );
    xmlclient->setRunner( this );
}

KJSEmbedPart::~KJSEmbedPart()
{
    if ( deletejs )
	delete js;
}

JSConsoleWidget *KJSEmbedPart::view()
{
    if ( !jsConsole ) {
	QCString name = widgetname ? widgetname : QCString("jsembed_console");
	jsConsole = new JSConsoleWidget( js, widgetparent, name );
	setWidget( jsConsole );
    }
    return jsConsole;
}

bool KJSEmbedPart::openURL( const KURL &url )
{
    if ( url.protocol() == "javascript" ) {
	kdDebug() << "KJSEmbedPart: openURL '" << url.url() << "' is javascript" << endl;

	QString cmd = url.url();
	QString js( "javascript:" );
	cmd = cmd.replace( 0, js.length(), QString("") );

	kdDebug() << "KJSEmbedPart: JS command is '" << cmd << "'" << endl;
	return jsConsole->execute( cmd );
    }
    return false;
}

bool KJSEmbedPart::loadScript( const QString &name )
{
    kdDebug() << "KJSEmbedPart::loadScript(): Script name is '" << name << "'" << endl;

    QFile file( name );
    if ( file.open(IO_ReadOnly) ) {

	QString script( file.readAll() );
	KJS::Completion res = js->evaluate( script, js->globalObject() );
	if ( (res.complType() == KJS::Normal) || (res.complType() == KJS::ReturnValue) )
	    return true;
    }

    return false;
}

bool KJSEmbedPart::executeScript( const QString &script )
{
    KJS::Completion res = js->evaluate( script, global );
    if ( (res.complType() == KJS::Normal) || (res.complType() == KJS::ReturnValue) )
	return true;

    return false;
}

bool KJSEmbedPart::loadActionSet( const QString &file )
{
    return xmlclient->load( file );
}

bool KJSEmbedPart::run( KJSEmbed::XMLActionClient *client, const KJSEmbed::XMLActionScript &script )
{
    if ( script.type == "js" ) {
	bool ok = false;

	if ( !script.src.isEmpty() ) {
	    ok = loadScript( script.src );
	}
	if ( !script.text.isEmpty() )
	    ok = executeScript( script.text );

	return ok;
    }
    else
	return XMLActionRunner::run( client, script );
}

KJS::Interpreter *KJSEmbedPart::createInterpreter()
{
    KJS::Object global( new KJS::ObjectImp() );
    KJS::Interpreter *js = new KJS::Interpreter( global );
    return js;
}

KJS::Object KJSEmbedPart::bind( QObject *obj )
{
    JSObjectProxy *proxy = new JSObjectProxy( js, obj );
    KJS::Object o(proxy);
    proxy->addBindings( js->globalExec(), o );
    return o;
}

void KJSEmbedPart::publish( QObject *obj, KJS::Object &parent, const char *name )
{
    if ( obj && js ) {
	KJS::Object jsobj = bind( obj );
	parent.put( js->globalExec(), name ? name : obj->name(), jsobj );
    }
    else {
	kdWarning() << "KJSEmbedPart::publish(...) Either the obj or js was 0" << endl;
    }

    if ( jsConsole ) {
	QString s("binding %1, %2");
	jsConsole->print( s.arg(obj->name()).arg(obj->className()) );
    }
}

void KJSEmbedPart::publish( QObject *obj, const char *name )
{
    publish( obj, global, name );
}

QObject *KJSEmbedPart::create( QObject *parent, const char *name, const char *cn ) 
{
    QString cname(cn);

    // Actions
    if ( cname == "KAction" )
	return new KAction( parent, name );
    else if ( cname == "KToggleAction" )
	return new KToggleAction( parent, name );

    // Widgets
    QWidget *pw=0;
    if ( parent && parent->isWidgetType() ) {
	pw = static_cast<QWidget *>( parent );

	QWidgetFactory wf;
	return wf.createWidget( cn, pw, name );
    }
    return 0;
}

QWidget *KJSEmbedPart::loadUI( const QString &uiFile, QObject *connector, QWidget *parent, const char *name )
{
    return QWidgetFactory::create( uiFile, connector, parent, name );
}

//
// JS Bindings
//

void KJSEmbedPart::publishStdBindings()
{
    publishStdBindings( js->globalExec(), global );
}

void KJSEmbedPart::publishStdBindings( KJS::ExecState *state, KJS::Object &parent )
{
    // Stdio
    parent.put( state ,"print", KJS::Object( new MethodImp( this, MethodImp::MethodPrint ) ) );
    parent.put( state ,"warn", KJS::Object( new MethodImp( this, MethodImp::MethodWarn ) ) );
    parent.put( state ,"readLine", KJS::Object( new MethodImp( this, MethodImp::MethodReadLine ) ) );

    // Factory
    KJS::Object object( new KJS::ObjectImp() );
    parent.put( state, "Factory", object );

    object.put( state ,"create", KJS::Object( new MethodImp( this, MethodImp::MethodCreate ) ) );
    object.put( state ,"loadui", KJS::Object( new MethodImp( this, MethodImp::MethodLoadUI ) ) );
    object.put( state ,"load", KJS::Object( new MethodImp( this, MethodImp::MethodLoadScript ) ) );
}

KJSEmbedPart::MethodImp::MethodImp( KJSEmbedPart *jsp, MethodId mid ) : ObjectImp(), part(jsp), id(mid) {}
KJSEmbedPart::MethodImp::~MethodImp() {}

KJS::Value KJSEmbedPart::MethodImp::call( KJS::ExecState *state,
					   KJS::Object &/*self*/, const KJS::List &args )
{
//    kdDebug() << "KJSEmbedPart::MethodImp " << (int) id << " called" << endl;

    if ( id == MethodCreate ) {

	QString cls = args[0].toString(state).qstring();
	QString name = args[1].toString(state).qstring();

	QObject *obj = part->create( 0, name.latin1(), cls.latin1() );

	KJS::Object proxy = part->bind( obj );
	return proxy;

    }
    else if ( id == MethodLoadUI ) {

	QString file = args[0].toString(state).qstring();
	QString name = args[1].toString(state).qstring();

	kdDebug() << "KJSEmbedPart::MethodImp: Loading UI '" << file << "'" << endl;
	QWidget *obj = part->loadUI( file, 0, 0, name.latin1() );
	if ( obj ) {
	    KJS::Object proxy = part->bind( obj );
	    return proxy;
	}
	else {
	    kdDebug() << "KJSEmbedPart::MethodImp: LoadUI failed for '" << file << "'" << endl;
	}

    }
    else if ( id == MethodLoadScript ) {

	QString name = args[0].toString(state).qstring();
	kdDebug() << "KJSEmbedPart::MethodImp: Loading script '" << name << "'" << endl;

	bool ok = part->loadScript( name );
	if ( !ok ) {
	    kdDebug() << "KJSEmbedPart::MethodImp: Loading script failed for '" << name << "'" << endl;
	}

	return KJS::Boolean( ok );
    }
    else if ( id == MethodPrint ) {

	QString msg = args[0].toString(state).qstring();
	QTextStream out( stdout, IO_WriteOnly );
	out << msg << endl;

	return KJS::Boolean( true );
    }
    else if ( id == MethodWarn ) {

	QString msg = args[0].toString(state).qstring();
	QTextStream err( stderr, IO_WriteOnly );
	err << msg << endl;

	return KJS::Boolean( true );
    }
    else if ( id == MethodReadLine ) {

	QTextStream in( stdin, IO_ReadOnly );
	QString line = in.readLine();
	if ( line == QString::null )
	    return KJS::Null();

	return KJS::String( line );
    }
    else {
	kdDebug() << "KJSEmbedPart::MethodImp has no such impl" << endl;
    }

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


}; // namespace KJSEmbed

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

