/*
 * $Id: kdcopwindow.cpp,v 1.9 2001/01/31 16:08:47 rich Exp $
 *
 * Copyright (C) 2000 by Matthias Kalle Dalheimer <kalle@kde.org>
 *
 * Licensed under the Artistic License.
 */

#include "kdcopwindow.h"

#include <dcopclient.h>
#include <klocale.h>
#include <kdatastream.h>
#include <kstdaction.h>
#include <kaction.h>
#include <kapp.h>
#include <kmessagebox.h>

#include <qlistview.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qcheckbox.h>
#include <qlineedit.h>
#include <qvalidator.h>
#include <qpushbutton.h>
#include <qkeycode.h>
#include <qdialog.h>

#include <kdebug.h>

class DCOPBrowserItem : public QListViewItem
{
public:
  enum DCOPBrowserItemType { Application, Interface, Function };

  DCOPBrowserItem( DCOPBrowserItemType t, DCOPBrowserItem* parent,
                   QCString t0 = 0, QCString t1 = 0,
                   QCString t2 = 0) :
    QListViewItem( parent, t0, t1, t2 ) {
    _t = t;
    app = t0;
    obj = t1;
    func = t2;
  }
  DCOPBrowserItem( DCOPBrowserItemType t, QListView* parent,
                   QCString t0 = 0, QCString t1 = 0,
                   QCString t2 = 0 ) :
    QListViewItem( parent, t0, t1, t2 ) {
    _t = t;
    app = t0;
    obj = t1;
    func = t2;
  }

  DCOPBrowserItemType _t;
  QCString app;
  QCString obj;
  QCString func;
};



KDCOPWindow::KDCOPWindow( QWidget*, const char* name ) :
  KMainWindow( 0, name )
{
  dcop = kapp->dcopClient();
  dcop->attach();

  lv = new QListView( this );
  lv->addColumn( i18n("Application") );
  lv->addColumn( i18n("Interface") );
  lv->addColumn( i18n("Function") );
  lv->setRootIsDecorated( true );
  lv->setAllColumnsShowFocus( true );
  connect( lv, SIGNAL( doubleClicked( QListViewItem* ) ),
           this, SLOT( slotCallFunction( QListViewItem* ) ) );
  connect( lv, SIGNAL( currentChanged( QListViewItem* ) ),
           this, SLOT( slotCurrentChanged( QListViewItem* ) ) );

  setCentralWidget( lv );

  // set up the actions
  KStdAction::quit( this, SLOT( close() ), actionCollection() );
  exeaction = new KAction( i18n( "&Execute" ), CTRL + Key_E, this, SLOT( slotCallFunction() ),
                           actionCollection(), "execute" );
  exeaction->setEnabled( false );

  createGUI();

  fillApplications();

  setCaption( i18n("DCOP Browser") );
}


void KDCOPWindow::slotCurrentChanged( QListViewItem* i )
{
  DCOPBrowserItem* item = (DCOPBrowserItem*)i;

  if( item->_t == DCOPBrowserItem::Function )
    exeaction->setEnabled( true );
  else
    exeaction->setEnabled( false );
}


void KDCOPWindow::slotCallFunction()
{
  slotCallFunction( lv->currentItem() );
}


void KDCOPWindow::slotCallFunction( QListViewItem* it )
{
  DCOPBrowserItem* item = (DCOPBrowserItem*)it;

  if( item->_t != DCOPBrowserItem::Function )
    return;

  QString f = item->func; // Qt is better with unicode strings, so use one.

  int s = f.find( ' ');

  if( s < 0 )
    s = 0;
  else
    s++;

  f = f.mid( s );
  int left = f.find( '(' );
  int right = f.find( ')' );

  if( left < 0 ) {
    KMessageBox::error( 0, i18n( "No parameters found" ),
                        i18n( "DCOP Browser Error" ));
    return;
  }

  QStringList types;
  QStringList names;
  if ( left >0 && left + 1 < right - 1) {
    types = QStringList::split( ',', f.mid( left + 1, right - left - 1) );
    for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
      (*it).stripWhiteSpace();
  //  qDebug( "analyzing %s", (*it).latin1() );
      int s = (*it).find(' ');
      if ( s > 0 ) {
  //    qDebug( "right is %s", (*it).mid( s + 1 ).latin1() );
  //    qDebug( "left is %s", (*it).left( s ).latin1() );
        names.append( (*it).mid( s + 1 ) );
        (*it) = (*it).left( s );
      }
    }
  }

  //   if ( (int) types.count() != argc ) {
  //     qFatal( "arguments do not match" );
  //   }

  QByteArray data, replyData;
  QCString replyType;
  QDataStream arg(data, IO_WriteOnly);

  QDialog* mydialog = new QDialog( 0, "KDCOP parameter entry", true );
  mydialog->setCaption( QString( i18n("Call function %1") ).arg( item->func ) );
  QGridLayout* grid = new QGridLayout( mydialog, types.count() + 2, 3, 10 );
  QLabel* h1 = new QLabel( i18n( "Name" ), mydialog );
  grid->addWidget( h1, 0, 0 );
  QLabel* h2 = new QLabel( i18n( "Type" ), mydialog );
  grid->addWidget( h2, 0, 1 );
  QLabel* h3 = new QLabel( i18n( "Value" ), mydialog );
  grid->addWidget( h3, 0, 2 );

  // Build up a dialog for parameter entry if there are any parameters.
  if( types.count() ) {
    int i = 0;
    QList<QWidget> wl;
    for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
      i++;
      QString type = *it;
      if( type == "int" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "int", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
        e->setValidator( new QIntValidator( e ) );
      }
      else if( type == "unsigned" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "unsigned", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );

        QIntValidator* iv = new QIntValidator( e );
        iv->setBottom( 0 );
        e->setValidator( iv );
      }
      else if ( type == "long" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "long", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
        e->setValidator( new QIntValidator( e ) );
      }
      else if ( type == "float" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "float", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
        e->setValidator( new QDoubleValidator( e ) );
      }
      else if ( type == "double" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "double", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
        e->setValidator( new QDoubleValidator( e ) );
      }
      else if ( type == "bool" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "bool", mydialog );
        grid->addWidget( l, i, 1 );
        QCheckBox* c = new QCheckBox( mydialog );
        grid->addWidget( c, i, 2 );
        wl.append( c );
      }
      else if ( type == "QString" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "QString", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
      }
      else if ( type == "QCString" ) {
        QLabel* n = new QLabel( names[i-1], mydialog );
        grid->addWidget( n, i, 0 );
        QLabel* l = new QLabel( "QString", mydialog );
        grid->addWidget( l, i, 1 );
        QLineEdit* e = new QLineEdit( mydialog );
        grid->addWidget( e, i, 2 );
        wl.append( e );
      }
      else
        qFatal( "cannot handle datatype '%s'", type.latin1() );
    }

    if( wl.count() > 0 )
      wl.at( 0 )->setFocus();

    i++;
    QPushButton* ok = new QPushButton( i18n( "OK" ), mydialog );
    grid->addWidget( ok, i, 0 );
    QObject::connect( ok, SIGNAL( clicked() ), mydialog, SLOT( accept() ) );
    ok->setDefault( true );
    QPushButton* cancel = new QPushButton( i18n( "Cancel" ), mydialog );
    grid->addWidget( cancel, i, 1 );
    QObject::connect( cancel, SIGNAL( clicked() ), mydialog, SLOT( reject() ) );

    int ret = mydialog->exec();
    if( ret == QDialog::Accepted ) {
      // extract the arguments
      int i = 0;
      for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
        QString type = *it;
        if( type == "int" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << e->text().toInt();
        } else if( type == "long" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << e->text().toLong();
        } else if( type == "float" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << e->text().toFloat();
        } else if( type == "double" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << e->text().toDouble();
        } else if( type == "bool" ) {
          QCheckBox* c = (QCheckBox*)wl.at( i );
          arg << c->isChecked();
        } else if( type == "QCString" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << QCString( e->text().latin1() );
        } else if( type == "QString" ) {
          QLineEdit* e = (QLineEdit*)wl.at( i );
          arg << e->text();
        } else
          qFatal( "Cannot handle datatype %s", type.latin1() );
        i++;
      }
    }
  }

  QString fsig = f.left(left) + "(" + types.join(",") + ")";

  // Now do the DCOP call
  if ( !dcop->call( item->app, item->obj, fsig.latin1(),  data, replyType, replyData) ) {
    kdDebug() << "call failed( " << item->app.data() << ", " << item->obj.data() << ", " << f << " )" << endl;
  } else {
    if( replyType != "void" && replyType != "ASYNC" && replyType != "" ) {
      QDataStream reply(replyData, IO_ReadOnly);
      if ( replyType == "int" ) {
        int i;
        reply >> i;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( i ),
                                  i18n( "Result" ) );
      } else if ( replyType == "long" ) {
        long l;
        reply >> l;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( l ),
                                  i18n( "Result" ) );
      } else if ( replyType == "float" ) {
        float f;
        reply >> f;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( f ),
                                  i18n( "Result" ) );
      } else if ( replyType == "double" ) {
        double d;
        reply >> d;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( d ),
                                  i18n( "Result" ) );
      } else if (replyType == "bool") {
        bool b;
        reply >> b;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( b ),
                                  i18n( "Result" ) );
      } else if (replyType == "QString") {
        QString r;
        reply >> r;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( r ),
                                  i18n( "Result" ) );
      } else if (replyType == "QCString") {
        QCString r;
        reply >> r;
        KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %2") ).arg( replyType ).arg( r ),
                                  i18n( "Result" ) );
      }
      else
      KMessageBox::information( 0, QString( i18n("Result type = %1<br>Result = %1(...)") ).arg( replyType ).arg( replyType ),
                                  i18n( "Result" ) );

    } else
      KMessageBox::information( 0, i18n( "DCOP call executed" ),
                                i18n( "Result" ) );
  }
}


void KDCOPWindow::fillFunctions( DCOPBrowserItem* item, const char* app, const char* obj )
{
  if( !strcmp( app, "kicker" ) && !strcmp( obj, "appletArea" ) )
    return;
  bool ok = false;
  QCStringList funcs = dcop->remoteFunctions( app, obj, &ok );
  if( ok )
    for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) {
      if ( (*it) == "QCStringList functions()" )
        continue;
      new DCOPBrowserItem( DCOPBrowserItem::Function,
                           item, app, obj, (*it).data() );
    }
}

void KDCOPWindow::fillObjects( DCOPBrowserItem* item, const char* app )
{
  if( !qstrncmp( app, "klauncher", 9 ) )
    return;
  bool ok = false;
  QCStringList objs = dcop->remoteObjects( app, &ok );
  for ( QCStringList::Iterator it = objs.begin(); it != objs.end(); ++it ) {
    DCOPBrowserItem* oitem;
    if( (*it) == "default" && ++it != objs.end() )
      oitem = new DCOPBrowserItem( DCOPBrowserItem::Interface,
                                   item, app, QString( "%1 (default)" ).arg( (*it).data() ).latin1() );
    else
      oitem = new DCOPBrowserItem( DCOPBrowserItem::Interface, item, app, (*it).data() );
    fillFunctions( oitem, app, (*it).data() );
  }
}

#include <stdio.h>

bool KDCOPWindow::fillApplications()
{
  QCStringList apps = dcop->registeredApplications();
  for ( QCStringList::Iterator it = apps.begin(); it != apps.end(); ++it )
    if ( (*it) != dcop->appId() && (*it).left(9) != "anonymous" )
      {
        DCOPBrowserItem* item = new DCOPBrowserItem( DCOPBrowserItem::Application,
                                                     lv, (*it).data() );
        fillObjects( item, (*it).data() );
      }
  return dcop->isAttached();
}

#include "kdcopwindow.moc"
