/*
  Netscape Plugin Loader KPart

  Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
                     Stefan Schimanski <1Stein@gmx.de>
  Copyright (c) 2002-2005 George Staikos <staikos@kde.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <dcopclient.h>
#include <kaboutdata.h>
#include <kaction.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kfiledialog.h>
#include <kio/netaccess.h>
#include <klocale.h>
#include <kparts/browserinterface.h>
#include <kparts/browserextension.h>

#include <tqlabel.h>

#include "nspluginloader.h"
#include "plugin_part.h"


class PluginBrowserExtension : public KParts::BrowserExtension
{
    friend class PluginPart;
public:
    PluginBrowserExtension( KParts::ReadOnlyPart *parent,
                          const char *name = 0L )
        : KParts::BrowserExtension( parent, name ) {}
    ~PluginBrowserExtension() {}

    // ATTENTION: you -CANNOT- add data members here
};


PluginLiveConnectExtension::PluginLiveConnectExtension(PluginPart* part) 
: KParts::LiveConnectExtension(part), _part(part), _retval(0L) {
}

PluginLiveConnectExtension::~PluginLiveConnectExtension() {
}

bool PluginLiveConnectExtension::get(const unsigned long, const TQString &field, Type &type, unsigned long &retobj, TQString &value) {
Q_UNUSED(type);
Q_UNUSED(retobj);
Q_UNUSED(value);
    kdDebug(1432) << "PLUGIN:LiveConnect::get " << field << endl;
    return false;
}

bool PluginLiveConnectExtension::call(const unsigned long, const TQString &func, const TQStringList &args, Type &type, unsigned long &retobjid, TQString &value) {
Q_UNUSED(type);
Q_UNUSED(retobjid);
Q_UNUSED(value);
    kdDebug(1432) << "PLUGIN:LiveConnect::call " << func << " args: " << args << endl;
    return false;
}

bool PluginLiveConnectExtension::put( const unsigned long, const TQString &field, const TQString &value) {
    kdDebug(1432) << "PLUGIN:LiveConnect::put " << field << " " << value << endl;
    if (_retval && field == "__nsplugin") {
        *_retval = value;
        return true;
    } else if (field.lower() == "src") {
        _part->changeSrc(value);
        return true;
    }
    return false;
}

TQString PluginLiveConnectExtension::evalJavaScript( const TQString & script )
{
    kdDebug(1432) << "PLUGIN:LiveConnect::evalJavaScript " << script << endl;
    ArgList args;
    TQString jscode;
    jscode.sprintf("this.__nsplugin=eval(\"%s\")",  TQString(script).tqreplace('\\', "\\\\").tqreplace('"', "\\\"").latin1());
    //kdDebug(1432) << "String is [" << jscode << "]" << endl;
    args.push_back(qMakePair(KParts::LiveConnectExtension::TypeString, jscode));
    TQString nsplugin("Undefined");
    _retval = &nsplugin;
    emit partEvent(0, "eval", args);
    _retval = 0L;
    return nsplugin;
}

extern "C"
{
  /**
   * This function is the 'main' function of this part.  It takes
   * the form 'void *init_lib<library name>()  It always returns a
   * new factory object
   */

   K_EXPORT_COMPONENT_FACTORY(libnsplugin,PluginFactory)
}


NSPluginCallback::NSPluginCallback(PluginPart *part)
  : DCOPObject()
{
    _part = part;
}


void NSPluginCallback::postURL(TQString url, TQString target, TQByteArray data, TQString mime)
{
    _part->postURL( url, target, data, mime );
}


void NSPluginCallback::requestURL(TQString url, TQString target)
{
    _part->requestURL( url, target );
}


void NSPluginCallback::statusMessage( TQString msg )
{
    _part->statusMessage( msg );
}


void NSPluginCallback::evalJavaScript( int id, TQString script )
{
    _part->evalJavaScript( id, script );
}
/**
 * We need one static instance of the factory for our C 'main'
 * function
 */
KInstance *PluginFactory::s_instance = 0L;


PluginFactory::PluginFactory()
{
    kdDebug(1432) << "PluginFactory::PluginFactory" << endl;
    s_instance = 0;

    // preload plugin loader
    _loader = NSPluginLoader::instance();
}


PluginFactory::~PluginFactory()
{
   kdDebug(1432) << "PluginFactory::~PluginFactory" << endl;

   _loader->release();

   if ( s_instance ) {
       delete s_instance->aboutData();
       delete s_instance;
   }
   s_instance = 0;
}

KParts::Part * PluginFactory::createPartObject(TQWidget *tqparentWidget, const char *widgetName,
                                         TQObject *parent, const char *name,
                                         const char *classname, const TQStringList &args)
{
    Q_UNUSED(classname)
    kdDebug(1432) << "PluginFactory::create" << endl;
    KParts::Part *obj = new PluginPart(tqparentWidget, widgetName, parent, name, args);
    return obj;
}


KInstance *PluginFactory::instance()
{
    kdDebug(1432) << "PluginFactory::instance" << endl;

    if ( !s_instance )
        s_instance = new KInstance( aboutData() );
    return s_instance;
}

KAboutData *PluginFactory::aboutData()
{
    KAboutData *about = new KAboutData("plugin", I18N_NOOP("plugin"), "1.99");
    return about;
}


/**************************************************************************/


PluginPart::PluginPart(TQWidget *tqparentWidget, const char *widgetName, TQObject *parent,
                       const char *name, const TQStringList &args)
    : KParts::ReadOnlyPart(parent, name), _widget(0), _args(args),
      _destructed(0L)
{
    setInstance(PluginFactory::instance());
    kdDebug(1432) << "PluginPart::PluginPart" << endl;

    // we have to keep the class name of KParts::PluginBrowserExtension
    // to let khtml find it
    _extension = static_cast<PluginBrowserExtension*>(new KParts::BrowserExtension(this));
    _liveconnect = new PluginLiveConnectExtension(this);

    // Only create this if we have no parent since the parent part is
    // responsible for "Save As" then
    if (!parent || !parent->inherits("Part")) {
        new KAction(i18n("&Save As..."), CTRL+Key_S, this, TQT_SLOT(saveAs()), actionCollection(), "saveDocument");
        setXMLFile("nspluginpart.rc");
    }

    // create
    _loader = NSPluginLoader::instance();
    _callback = new NSPluginCallback(this);

    // create a canvas to insert our widget
    _canvas = new PluginCanvasWidget( tqparentWidget, widgetName );
    //_canvas->setFocusPolicy( TQWidget::ClickFocus );
    _canvas->setFocusPolicy( TQ_WheelFocus );
    _canvas->setBackgroundMode( TQWidget::NoBackground );
    setWidget(_canvas);
    _canvas->show();
    TQObject::connect( _canvas, TQT_SIGNAL(resized(int,int)),
                      this, TQT_SLOT(pluginResized(int,int)) );
}


PluginPart::~PluginPart()
{
    kdDebug(1432) << "PluginPart::~PluginPart" << endl;

    delete _callback;
    _loader->release();
    if (_destructed)
        *_destructed = true;
}


bool PluginPart::openURL(const KURL &url)
{
    closeURL();
    kdDebug(1432) << "-> PluginPart::openURL" << endl;

    m_url = url;
    TQString surl = url.url();
    TQString smime = _extension->urlArgs().serviceType;
    bool reload = _extension->urlArgs().reload;
    bool embed = false;
    bool post = _extension->urlArgs().doPost();

    // handle arguments
    TQStringList argn, argv;

    TQStringList::Iterator it = _args.begin();
    for ( ; it != _args.end(); ) {

        int equalPos = (*it).tqfind("=");
        if (equalPos>0) {

            TQString name = (*it).left(equalPos).upper();
            TQString value = (*it).mid(equalPos+1);
            if (value[0] == '"' && value[value.length()-1] == '"')
                value = value.mid(1, value.length()-2);

            kdDebug(1432) << "name=" << name << " value=" << value << endl;

            if (!name.isEmpty()) {
                // hack to pass view mode from khtml
                if ( name=="__KHTML__PLUGINEMBED" ) {
                    embed = true;
                    kdDebug(1432) << "__KHTML__PLUGINEMBED found" << endl;
                } else {
                    argn << name;
                    argv << value;
                }
            }
        }

        it++;
    }

    if (surl.isEmpty()) {
        kdDebug(1432) << "<- PluginPart::openURL - false (no url passed to nsplugin)" << endl;
        return false;
    }

    // status messages
    emit setWindowCaption( url.prettyURL() );
    emit setStatusBarText( i18n("Loading Netscape plugin for %1").arg(url.prettyURL()) );

    // create plugin widget
    NSPluginInstance *inst = _loader->newInstance(_canvas, surl, smime, embed, argn, argv, kapp->dcopClient()->appId(), _callback->objId(), reload, post, _extension->urlArgs().postData);

    if ( inst ) {
        _widget = inst;
    } else {
        TQLabel *label = new TQLabel( i18n("Unable to load Netscape plugin for %1").arg(url.url()), _canvas );
        label->tqsetAlignment( AlignCenter | WordBreak );
        _widget = label;
    }

    _widget->resize(_canvas->width(), _canvas->height());
    _widget->show();

    kdDebug(1432) << "<- PluginPart::openURL = " << (inst!=0) << endl;
    return inst != 0L;
}


bool PluginPart::closeURL()
{
    kdDebug(1432) << "PluginPart::closeURL" << endl;
    delete _widget;
    _widget = 0;
    return true;
}


void PluginPart::reloadPage()
{
    kdDebug(1432) << "PluginPart::reloadPage()" << endl;
    _extension->browserInterface()->callMethod("goHistory(int)", 0);
}

void PluginPart::postURL(const TQString& url, const TQString& target, const TQByteArray& data, const TQString& mime)
{
    kdDebug(1432) << "PluginPart::postURL( url=" << url
                  << ", target=" << target << endl;

    KURL new_url(this->url(), url);
    KParts::URLArgs args;
    args.setDoPost(true);
    args.frameName = target;
    args.postData = data;
    args.setContentType(mime);

    emit _extension->openURLRequest(new_url, args);
}

void PluginPart::requestURL(const TQString& url, const TQString& target)
{
    kdDebug(1432) << "PluginPart::requestURL( url=" << url
                  << ", target=" << target << endl;

    KURL new_url(this->url(), url);
    KParts::URLArgs args;
    args.frameName = target;
    args.setDoPost(false);

    emit _extension->openURLRequest(new_url, args);
}

void PluginPart::evalJavaScript(int id, const TQString & script)
{
    kdDebug(1432) <<"evalJavascript: before widget check"<<endl;
    if (_widget) {
        bool destructed = false;
        _destructed = &destructed;
	kdDebug(1432) <<"evalJavascript: there is a widget" <<endl;	
        TQString rc = _liveconnect->evalJavaScript(script);
        if (destructed)
            return;
        _destructed = 0L;
        kdDebug(1432) << "Liveconnect: script [" << script << "] evaluated to [" << rc << "]" << endl;
        NSPluginInstance *ni = dynamic_cast<NSPluginInstance*>(_widget.operator->());
        if (ni)
            ni->javascriptResult(id, rc);
    }
}

void PluginPart::statusMessage(TQString msg)
{
    kdDebug(1422) << "PluginPart::statusMessage " << msg << endl;
    emit setStatusBarText(msg);
}


void PluginPart::pluginResized(int w, int h)
{
    kdDebug(1432) << "PluginPart::pluginResized()" << endl;

    if (_widget) {
        _widget->resize(w, h);
    }
}


void PluginPart::changeSrc(const TQString& url) {
    closeURL();
    openURL(KURL( url ));
}


void PluginPart::saveAs() {
    KURL savefile = KFileDialog::getSaveURL(TQString::null, TQString::null, _widget);
    KIO::NetAccess::copy(m_url, savefile, _widget);
}


void PluginCanvasWidget::resizeEvent(TQResizeEvent *ev)
{
    TQWidget::resizeEvent(ev);
    emit resized(width(), height());
}


#include "plugin_part.moc"