// -*- C++ -*-

//
//  klpq
//
//  Copyright (C) 1997 Christoph Neerfeld
//  email:  Christoph.Neerfeld@home.ivm.de or chris@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., 675 Mass Ave, Cambridge, MA 02139, USA.
//

extern "C" {
#include <unistd.h>
};

#include <qcheckbox.h>
#include <qcombobox.h>
#include <qframe.h>
#include <qgroupbox.h>
#include <qlabel.h>
#include <qlistbox.h>
#include <qlistview.h>
#include <qpushbutton.h>
#include <qkeycode.h>
#include <qcollection.h>
#include <qfileinfo.h>
#include <qdir.h>
#include <qcursor.h>
#include <qevent.h>
#include <qdragobject.h>

#include <kprocess.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kconfig.h>
#include <kmenubar.h>
#include <kstdaccel.h>

#include "klpq.h"
#include "klpq.moc"
#include "queueview.h"
#include "klpqspooler.h"
#include "FirstStart.h"

Klpq::Klpq( const char *name )
  : KTMainWindow( name )
{
  update_delay = 0; // means no auto update at all
  KConfig *config = KApplication::kApplication()->config();
  config->setGroup("klpq");
  if( config->hasKey("updateDelay") )
    update_delay = config->readNumEntry("updateDelay");

  conf_auto = new ConfAutoUpdate( topLevelWidget(), "update" );
  conf_auto->setFrequency(update_delay);

  conf_spooler = new SpoolerConfig( topLevelWidget(), "spooler" );

  f_main = new QFrame( this, "Frame_1" );

  f_top = new QGroupBox(i18n("Printer Configuration"), f_main, "Frame_2" );

  QVBoxLayout *vlayout = new QVBoxLayout(f_top, KDialog::marginHint(),
					 KDialog::spacingHint());
  vlayout->addSpacing(f_top->fontMetrics().lineSpacing());

  QHBoxLayout *hlayout = new QHBoxLayout;
  vlayout->addLayout(hlayout);

  l_printer = new QLabel( f_top, "Label_1" );
  l_printer->setText( i18n("Printer name:") );
  hlayout->addWidget( l_printer, 0 );

  cb_printer = new QComboBox( f_top, "ComboBox_1" );
  hlayout->addWidget( cb_printer, 3, AlignLeft );

  c_queuing = new QCheckBox( f_top, "CheckBox_1" );
  c_queuing->setText( i18n("Queuing") );
  hlayout->addWidget( c_queuing, 1 );

  c_printing = new QCheckBox( f_top, "CheckBox_2" );
  c_printing->setText( i18n("Printing") );
  hlayout->addWidget( c_printing, 1 );

  lb_list = new QListView(f_main);
  lb_list->addColumn(i18n("Rank"), 75);
  lb_list->addColumn(i18n("Owner"), 150);
  lb_list->addColumn(i18n("Job"), 75);
  lb_list->addColumn(i18n("Files"), 150);
  lb_list->addColumn(i18n("Size"), 75);

  //  connect( lb_list, SIGNAL(rightButtonClicked()), this, SLOT(popupMenu()) );

  top2bottom = new QVBoxLayout( f_main, KDialog::marginHint(),
				KDialog::spacingHint());
  top2bottom->addWidget( f_top );
  top2bottom->addWidget( lb_list, 1 );

  hlayout = new QHBoxLayout;
  top2bottom->addLayout(hlayout);

  vlayout = new QVBoxLayout;
  hlayout->addLayout(vlayout);

  b_remove = new QPushButton( f_main, "PushButton_3" );
  b_remove->setText( i18n("Remove") );
  vlayout->addWidget(b_remove);

  b_make_top = new QPushButton( f_main, "PushButton_4" );
  b_make_top->setText( i18n("Make Top") );
  vlayout->addWidget(b_make_top);

  lb_status = new QListBox( f_main, "ListBox_2" );
  lb_status->setMinimumSize( 312, 44 );
  hlayout->addWidget(lb_status);

  vlayout = new QVBoxLayout;
  hlayout->addLayout(vlayout);

  b_auto = new QPushButton( f_main, "PushButton_6" );
  b_auto->setText( i18n("Auto") );
  b_auto->setToggleButton( TRUE );
  vlayout->addWidget(b_auto);

  b_update = new QPushButton( f_main, "PushButton_1" );
  b_update->setText( i18n("Update") );
  vlayout->addWidget( b_update );

  QPopupMenu *file = new QPopupMenu;
  file->insertItem(i18n("&Quit"), qApp, SLOT(quit()), KStdAccel::key(KStdAccel::Quit) );

  QPopupMenu *conf_menu = new QPopupMenu;
  conf_menu->insertItem(i18n("&Update frequency"),this,SLOT(configureAuto()));
  conf_menu->insertItem(i18n("&Spooler"), this, SLOT(configureSpooler()) );

  QString about = i18n(""
    "Klpq %1\n(C) by Christoph Neerfeld\nchris@kde.org").arg(KLPQ_VERSION);
  menuBar()->insertItem( i18n("&File"), file );
  menuBar()->insertItem( i18n("&Options"), conf_menu );
  menuBar()->insertSeparator();
  menuBar()->insertItem( i18n("&Help"), helpMenu( about ) );

  connect( cb_printer, SIGNAL(activated(int)), this, SLOT(printerSelect(int)));
  connect( c_queuing, SIGNAL(clicked()), this, SLOT(queuing()) );
  connect( c_printing, SIGNAL(clicked()), this, SLOT(printing()) );
  connect( b_update, SIGNAL(clicked()), this, SLOT(update()) );
  connect( b_remove, SIGNAL(clicked()), this, SLOT(remove()) );
  connect( b_make_top, SIGNAL(clicked()), this, SLOT(makeTop()) );
  connect( b_auto, SIGNAL(clicked()), this, SLOT(setAuto()) );

  popup_menu = new QPopupMenu;
  popup_menu->insertItem( i18n("Remove"), this, SLOT(remove()) );
  popup_menu->insertItem( i18n("Make Top"), this, SLOT(makeTop()) );

  int key_id;
  accel = new QAccel(this);
  accel->connectItem( accel->insertItem( Key_U ), this, SLOT(update()) );
  accel->connectItem( accel->insertItem( Key_R ), this, SLOT(remove()) );
  accel->connectItem( accel->insertItem( Key_T ), this, SLOT(makeTop()) );
  key_id = accel->insertItem( Key_A );
  accel->connectItem( key_id, b_auto, SLOT(toggle()) );
  accel->connectItem( key_id, this, SLOT(setAuto()) );
  accel->connectItem( accel->insertItem( Key_Left ), this, SLOT(prevPrinter()) );
  accel->connectItem( accel->insertItem( Key_Right ), this, SLOT(nextPrinter()) );
  //accel->connectItem( accel->insertItem( Key_E ), this, SLOT(queuing()) );
  //accel->connectItem( accel->insertItem( Key_I ), this, SLOT(printing()) );

  setView(f_main, FALSE);

  // set up subprocess
  QString temp;
  FirstStart spooler_dialog;
  if( (temp = config->readEntry("Spooler") ) == "BSD" )
    spooler = new SpoolerBsd;
  else if( temp == "PPR" )
    spooler = new SpoolerPpr;
  else if ( temp == "LPRNG" )
    spooler = new SpoolerLprng;
  else
    {
      spooler_dialog.exec();
      temp = spooler_dialog.getSpooler();
      config->writeEntry("Spooler", temp);
      if( temp == "PPR" )
	spooler = new SpoolerPpr;
      else if ( temp == "LPRNG" )
	spooler = new SpoolerLprng;
      else
	spooler = new SpoolerBsd;
    }
  lpc = new KProcess;
  lpc_com = NotRunning;
  connect( lpc, SIGNAL(receivedStdout(KProcess *, char *, int)),
	   this, SLOT(recvLpc(KProcess *, char *, int)) );
  connect( lpc, SIGNAL(processExited(KProcess *)),
	   this, SLOT(exitedLpc(KProcess *)) );

  lpq = new KProcess;
  lpq_running = FALSE;
  connect( lpq, SIGNAL(receivedStdout(KProcess *, char *, int)),
	   this, SLOT(recvLpq(KProcess *, char *, int)) );
  connect( lpq, SIGNAL(processExited(KProcess *)),
	   this, SLOT(exitedLpq(KProcess *)) );

  // activate DnD
  setAcceptDrops(true);

  int width, height;
  config->setGroup("klpq");
  width = config->readNumEntry("Width");
  height = config->readNumEntry("Height");
  if( width < minimumSize().width() )
    width = minimumSize().width();
  if( height < minimumSize().height() )
    height = minimumSize().height();

  resize(f_main->sizeHint());

  b_auto->setOn( config->readBoolEntry("autoOn", TRUE) );
  setAuto();
}

Klpq::~Klpq()
{
  delete conf_auto;
  delete conf_spooler;

  delete lpc;
  delete lpq;

  KConfig &config = *kapp->config();
  config.setGroup("klpq");
  config.writeEntry("Width", width());
  config.writeEntry("Height", height());
  config.writeEntry("updateDelay", update_delay);
  config.writeEntry("autoOn", b_auto->isOn() );
  config.writeEntry("lastQueue", cb_printer->currentText() );
  config.sync();
}



void Klpq::setLastPrinterCurrent()
{
  if( cb_printer->count() )
    cb_printer->setCurrentItem( cb_printer->count() - 1 );
}

void Klpq::update()
{
  if( !isEnabled() )
    return;
  lpq_in_buffer = QString::null;
  lpq->clearArguments();

  spooler->updateCommand( lpq, cb_printer->text( cb_printer->currentItem() ) );
  if( (lpq_running = lpq->start( KProcess::NotifyOnExit, KProcess::Stdout )) )
    disable();
}

void Klpq::queuing()
{
  if( !isEnabled() )
    {
      if( c_queuing->isChecked() )
	c_queuing->setChecked(FALSE);
      else
	c_queuing->setChecked(TRUE);
      return;
    }
  lpc->clearArguments();
  spooler->setQueuingCommand( lpc, cb_printer->text( cb_printer->currentItem() ),
			      c_queuing->isChecked() );
  if( lpc->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
    {
      lpc_com = Queuing;
      disable();
    }
}

void Klpq::printing()
{
  if( !isEnabled() )
    {
      if( c_printing->isChecked() )
	c_printing->setChecked(FALSE);
      else
	c_printing->setChecked(TRUE);
      return;
    }
  lpc->clearArguments();
  spooler->setPrintingCommand( lpc, cb_printer->text( cb_printer->currentItem() ),
			       c_printing->isChecked() );
  if( lpc->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
    {
      lpc_com = Printing;
      disable();
    }
}

void Klpq::remove()
{
  if( !isEnabled() )
    return;
  int job = ((MyLVI *)lb_list->selectedItem())->jobId();
  QString word;
  word.setNum(job);
  QString ask;
  ask = i18n("Do you really want to remove job No %1?").arg(job);
  if( KMessageBox::questionYesNo(this, ask) == KMessageBox::Yes)
    {
      QString progname;
      progname = spooler->removeCommand( cb_printer->text( cb_printer->currentItem() ), word );
      system( progname );
      update();
    }
}

void Klpq::makeTop()
{
  if( !isEnabled() )
    return;
  int job = ((MyLVI *)lb_list->selectedItem())->jobId();
  QString word;
  word.setNum(job);

  QString msg = i18n(""
    "Do you really want to move job No %1\non top of queue?").arg(job);
  if( KMessageBox::questionYesNo( this, msg ) == KMessageBox::Yes )
  {
    lpc->clearArguments();
    spooler->makeTopCommand( lpc, cb_printer->text(cb_printer->currentItem()),
			     word );
    if( lpc->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
    {
      lpc_com = MakeTop;
      disable();
    }
  }
}

void Klpq::setAuto()
{
  if( update_delay == 0 )
    {
      b_auto->setOn( FALSE );
      b_auto->setEnabled(FALSE);
      return;
    } else
      b_auto->setEnabled(TRUE);

  if( b_auto->isOn() )
    {
      if( cb_printer->count() )
	update();
      QTimer::singleShot(update_delay * 1000, this, SLOT(setAuto()) );
    }
}


void Klpq::prevPrinter()
{
  if( !isEnabled() )
    return;
  int cur = cb_printer->currentItem();
  if( cur == 0 )
    return;
  cb_printer->setCurrentItem( cur - 1 );
  update();
}

void Klpq::nextPrinter()
{
  if( !isEnabled() )
    return;
  int cur = cb_printer->currentItem();
  if( ++cur == cb_printer->count() )
    return;
  cb_printer->setCurrentItem( cur );
  update();
}

void Klpq::configureAuto()
{
  if( conf_auto->exec() ) {
    update_delay = conf_auto->getFrequency();
    setAuto();
  } else
    conf_auto->setFrequency(update_delay);
}

void Klpq::configureSpooler()
{
  KConfig &config = *kapp->config();
  config.setGroup("klpq");
  conf_spooler->initConfig( config.readEntry("Spooler", "BSD") );
  if( conf_spooler->exec() )
  {
    QString spoolerName = conf_spooler->saveConfig();
    delete spooler;
    if( spoolerName == "BSD" )
      spooler = new SpoolerBsd;
    else if( spoolerName == "PPR" )
      spooler = new SpoolerPpr;
    else if ( spoolerName == "LPRNG" )
      spooler = new SpoolerLprng;
  }
}

void Klpq::enable()
{
  if( lpc_com != NotRunning || lpq_running )
    return;
  setEnabled(TRUE);
  QApplication::restoreOverrideCursor();
}

void Klpq::disable()
{
  QApplication::setOverrideCursor( waitCursor );
  setEnabled(FALSE);
}

void Klpq::updateList()
{
  QString temp;
  lb_list->setUpdatesEnabled(FALSE);
  lb_status->setUpdatesEnabled(FALSE);
  lb_list->clear();
  lb_status->clear();
  spooler->parseUpdate( lb_list, lb_status, lpq_in_buffer );
  lb_list->setUpdatesEnabled(TRUE);
  lb_status->setUpdatesEnabled(TRUE);

  int jobs = lb_list->childCount();
  if( jobs != 0 )
    lb_list->setSelected( lb_list->firstChild(), TRUE );

  b_remove->setEnabled( jobs != 0 );
  b_make_top->setEnabled( jobs != 0 );

  lb_list->repaint(FALSE);
  lb_status->repaint(TRUE);
  // --- exec lpc
  if( lpc_com == NotRunning )
    {
      lpc->clearArguments();
      spooler->statusCommand( lpc, cb_printer->text( cb_printer->currentItem() ) );
      if( lpc->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
	lpc_com = Status;
    }
}

void Klpq::recvLpq( KProcess *, char *buffer, int len )
{
  if( !lpq_running )
    {
      debug("lpq not running");
      return;
    }
  int i;
  for( i = 0; i < len; i++ )
    {
      lpq_in_buffer += *(buffer+i);
    }
}

void Klpq::recvLpc( KProcess *, char *buffer, int len )
{
  unsigned char c;
  int j = 0;
  QString temp;
  switch(lpc_com) {
  case NotRunning:
    debug("lpc not running");
    return;
  case Status:
    spooler->parseStatus( buffer, len, c_queuing, c_printing );
    break;
  case Queuing:
    if( spooler->parseQueuing( buffer, len ) < 0 )
      KMessageBox::error( this, i18n("This is a privileged command\n"
				     "and you do not have permission to execute it."),
				i18n("Permission denied"));
    break;
  case Printing:
    if( spooler->parsePrinting( buffer, len) < 0 )
      KMessageBox::error( this, i18n("This is a privileged command\n"
				     "and you do not have permission to execute it."),
				i18n("Permission denied"));
    break;
  case MakeTop:
    while( (c = *(buffer+j)) && j < len )
      {
	j++;
	if ( c >= 128) break;
	if ( c != '\n' )
	  temp += c;
	else
	  {
	    if(temp.contains("Privileged") || temp.contains("permission") )
	      {
		KMessageBox::error( this,
				      i18n("This is a privileged command\n"
					   "and you do not have permission to execute it."),
				      i18n("Permission denied"));
		return;
	      }
	    temp = "";
	  }
      }
    break;
  };
}

void Klpq::exitedLpc(KProcess *)
{
  if( lpc_com == Status )
    {
      lpc_com = NotRunning;
      enable();
      return;
    }
  lpc_com = NotRunning;
  enable();
  update();
}


void Klpq::dragEnterEvent(QDragEnterEvent* event)
{
  event->accept(QUrlDrag::canDecode(event));
}


void Klpq::dropEvent(QDropEvent* event)
{
  QString name;
  QStrList urls;

  if(!QUrlDrag::decode(event, urls))
    return;

  QStrListIterator it(urls);

  for( ; it.current(); ++it )
    {
      name = (QString) it.current();
      if( name.left(5) != "file:" )
	{
	  remote_files.append( (QString) cb_printer->text(cb_printer->currentItem()) + ":" + name);
	}
      else
	{
	  name = name.remove(0,5);
	  QString progname;
	  progname = (QString) "lpr -P " + cb_printer->text( cb_printer->currentItem() ) + " "
	    + name;
	  system( (const char *) progname);
	}
    }
  update();
  if( remote_files.count() )
    {
      // start download of files
      QString pid, current_file;
      pid.setNum(::getpid());
      current_file = "file:/tmp/klpq_download.";
      current_file += pid;
      QString temp = remote_files.first();
      temp = temp.remove(0, temp.find(':')+1);
      KIO::Job* iojob = KIO::copy(temp, current_file);
//      iojob->setGUImode ( KIOJob::NONE );
      connect( iojob, SIGNAL( result(KIO::Job *) ), this, SLOT( slotIOJobFinished( KIO::Job* ) ) );
    }
}

void Klpq::slotIOJobFinished( KIO::Job* )
{
  QString pid, current_file;
  pid.setNum(::getpid());
  current_file = "/tmp/klpq_download.";
  current_file += pid;
  QString queue = remote_files.first();
  QFileInfo fi(current_file);
  if( fi.exists() )
    {
      queue = queue.left( queue.find(':') );
      QString progname;
      if( queue.isEmpty() )
	progname = (QString) "lpr " + current_file;
      else
	progname = (QString) "lpr -P " + queue + " " + current_file;
      system( (const char *) progname);
      QDir dir("/tmp");
      dir.remove(current_file);
    }
  remote_files.removeFirst();
  if( remote_files.count() != 0 )
    {
      QString temp = remote_files.first();
      temp = temp.remove(0, temp.find(':')+1);
      KIO::Job* iojob = KIO::copy(temp, "file:" + current_file);
      connect( iojob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotIOJobFinished( KIO::Job* ) ) );
    }
  else
    {
      if( !isVisibleToTLW() )
	QApplication::exit();
      QTimer::singleShot( 2000, this, SLOT(update()) );
    }
}

void Klpq::printRemote( QStrList file_list )
{
  remote_files = file_list;
  QString pid, current_file;
  pid.setNum(::getpid());
  current_file = "file:/tmp/klpq_download.";
  current_file += pid;
  QString temp = remote_files.first();
  temp = temp.remove(0, temp.find(':')+1);
  KIO::Job * iojob = KIO::copy(temp, current_file);
  connect( iojob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotIOJobFinished( KIO::Job* ) ) );
}

void Klpq::popupMenu()
{
  popup_menu->popup(QCursor::pos());
}




