//////////////////////////////////////////////////////////////////////
//      $Id: kpackage.cpp,v 1.80 2002/10/09 15:19:03 toivo Exp $
// File  : kpackage.cpp
// Author: Damyan Pepper
//         Toivo Pedaste
//
// See kpackage.h for more information.
//////////////////////////////////////////////////////////////////////

#include "../config.h"
extern "C"
{
#include <time.h>
}
#include <unistd.h>

#include <qdir.h>
#include <qdragobject.h>

#include <kurl.h>
#include <kapplication.h>
#include <kaccel.h>
#include <kkeydialog.h>
#include <klocale.h>
#include <klineeditdlg.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <kstdaction.h>
#include <kedittoolbar.h>
#include <kmimemagic.h>

#include "kpackage.h"
#include "managementWidget.h"
#include "pkgOptions.h"
#include "kio.h"
#include "findf.h"
#include "search.h"
#include "options.h"
#include "cache.h"

extern Opts *opts;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
KPKG::KPKG(KConfig *_config)
  : KMainWindow(0)
{
  kpackage = new KPACKAGE(_config,this);
  setCentralWidget(kpackage);

  config =  kapp->config();
  config->setGroup("Kpackage");

  kpackage->management->readPSeparator();

  // Get a nice default size
  resize(600,400);

  setupMenu();
  disableNext();
  disablePrevious();

  optiondialog = new Options(this);

  prop_restart = false;


  int width, height;
  width = config->readNumEntry("Width", 760);
  height = config->readNumEntry("Height", 540);

  if( width < minimumSize().width() ) {
    width = minimumSize().width();
  }
  if( height < minimumSize().height() ) {
    height = minimumSize().height();
  }

  resize(width, height);

}

// Destructor
KPKG::~KPKG()
{
  //   object deletion - slows down exit
  //    delete filemenu;
  //    delete helpmenu;
  //    delete kp;
  //    delete options;
  //    delete optiondialog;
  //    delete caches;
}

// Set up the menu

void KPKG::setupMenu()
{


  keys = new KAccel(this);

  pack_open = KStdAction::open(kpackage, SLOT(fileOpen()),
			  actionCollection());
  pack_open->plugAccel(keys);

  recent = KStdAction::openRecent(this, SLOT(openRecent(const KURL&)),
				  actionCollection());
  recent->loadEntries( config );

 pack_find = new KAction( i18n("Find &Package..."), "find",
		       KStdAccel::key(KStdAccel::Find), kpackage,
		       SLOT(find()), actionCollection(), "pack_find");
  pack_find->plugAccel(keys);

  pack_findf = new KAction( i18n("Find &File..."), "filefind",
		       0, kpackage,
		       SLOT(findf()), actionCollection(), "pack_findf");
  pack_findf->plugAccel(keys);

  kpack_reload = new KAction( i18n("&Reload"), "reload",
		       KStdAccel::key(KStdAccel::Reload), kpackage,
		       SLOT(reload()), actionCollection(), "kpack_reload");
  kpack_reload->plugAccel(keys);

  (void) KStdAction::quit(kpackage, SLOT(fileQuit()),
			  actionCollection())->plugAccel(keys);

  pack_prev = KStdAction::back(kpackage->management, SLOT(previous()),
			  actionCollection(),"pack_prev");
  pack_prev->plugAccel(keys);

  pack_next = KStdAction::forward(kpackage->management, SLOT(next()),
			     actionCollection(),"pack_next");
  pack_next->plugAccel(keys);

  (void) (new KAction( i18n("&Expand Tree"), "ftout",
		       0, kpackage,
		       SLOT(expandTree()), actionCollection(), "kpack_expand"))->plugAccel(keys);

  (void) (new KAction( i18n("&Collapse Tree"), "ftin",
		       0, kpackage,
		       SLOT(collapseTree()), actionCollection(), "kpack_collapse"))->plugAccel(keys);

  (void) (new KAction( i18n("Clear &Marked"), QString::null,
		       0, kpackage,
		       SLOT(clearMarked()), actionCollection(), "kpack_clear"))->plugAccel(keys);

  pack_install = new KAction( i18n("&Install"), QString::null,
		       0, kpackage->management,
		       SLOT(installSingleClicked()), actionCollection(), "install_single");
  pack_install->plugAccel(keys);
  pack_install->setEnabled(false);
  kpackage->management->setInstallAction(pack_install);


  pack_uninstall = new KAction( i18n("&Uninstall"), QString::null,
		       0, kpackage->management,
		       SLOT(uninstallSingleClicked()), actionCollection(), "uninstall_single");
  pack_uninstall->plugAccel(keys);
  pack_uninstall->setEnabled(false);
  kpackage->management->setUninstallAction(pack_uninstall);


  (void) (new KAction( i18n("&Install Marked"), QString::null,
		       0, kpackage->management,
		       SLOT(installMultClicked()), actionCollection(), "install_marked"))->plugAccel(keys);

  (void) (new KAction( i18n("&Uninstall Marked"), QString::null,
		       0, kpackage->management,
		       SLOT(uninstallMultClicked()), actionCollection(), "uninstall_marked"))->plugAccel(keys);

  pack_toolbar = KStdAction::showToolbar( this, SLOT(toggleToolBar()), actionCollection());

  KStdAction::configureToolbars( this, SLOT(configureToolBars()),
				 actionCollection())->plugAccel(keys);

  KStdAction::saveOptions( this, SLOT(saveSettings()), actionCollection())->plugAccel(keys);

  KStdAction::keyBindings( this, SLOT(setKeys()), actionCollection())->plugAccel(keys);

  (void) (new KAction( i18n("Configure &Kpackage..."), "configure",
		       0, this,
		       SLOT(setOptions()), actionCollection(), "kpack_options"))->plugAccel(keys);

  (void) (new KAction( i18n("Clear Package &Directory Cache"), QString::null,
		       0, this,
		       SLOT(clearDCache()), actionCollection(), "clear_dcache"))->plugAccel(keys);

  (void) (new KAction( i18n("Clear &Package Cache"), QString::null,
		       0, this,
		       SLOT(clearPCache()), actionCollection(), "clear_pcache"))->plugAccel(keys);

  int i;
  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      kpinterface[i]->makeMenu(keys, actionCollection());
    }
  }

  int toolPos = config->readNumEntry("ToolBarPos",KToolBar::Left);
  int iconText = config->readNumEntry("IconText");
  int iconSize = config->readNumEntry("IconSize",0);

  keys->readSettings();
  //  urlList.setAutoDelete(TRUE);
  createGUI();

  toolBar()->setBarPos((KToolBar::BarPosition)toolPos);
  toolBar()->setIconText((KToolBar::IconText)iconText);
  toolBar()->setIconSize(iconSize);
}

void KPKG::disableMenu()
{
  pack_open->setEnabled(false);
  pack_find->setEnabled(false);
  pack_findf->setEnabled(false);
  kpack_reload->setEnabled(false);
  recent->setEnabled(false);
}

void KPKG::enableMenu()
{
  pack_open->setEnabled(true);
  pack_find->setEnabled(true);
  pack_findf->setEnabled(true);
  kpack_reload->setEnabled(true);
  recent->setEnabled(true);
}

void KPKG::disableNext() {
  pack_next->setEnabled(false);
}

void KPKG::enableNext() {
  pack_next->setEnabled(true);
}

void KPKG::disablePrevious() {
  pack_prev->setEnabled(false);
}

void KPKG::enablePrevious() {
  pack_prev->setEnabled(true);
}
void KPKG::openRecent(const KURL& url){
  kpackage->openNetFile( url );
}

void KPKG::add_recent_file(QString newfile){

  KURL url = KURL(newfile);

  recent->addURL( url );
}

void KPKG::toggleToolBar() {
  if(pack_toolbar->isChecked())
    toolBar("mainToolBar")->show();
  else
    toolBar("mainToolBar")->hide();
}

void  KPKG::configureToolBars() {
  KEditToolbar dlg(actionCollection());
  if (dlg.exec())
    createGUI();
}

void KPKG::writeSettings(){

  kpackage->management->writePSeparator();

  KConfig *config = kapp->config();

  config->setGroup("Kpackage");

  config->writeEntry("Width", width());
  config->writeEntry("Height", height());
  config->writeEntry( "ToolBar", hide_toolbar ? "off" : "on" );

  // Bars created floating don't work
  if (toolBar()->barPos() == KToolBar::Floating)
    config->writeEntry("ToolBarPos", (int)KToolBar::Left );
  else
    config->writeEntry("ToolBarPos", (int) toolBar()->barPos() );
  config->writeEntry("IconText", (int) toolBar()->iconText() );
  config->writeEntry("IconSize", (int) toolBar()->iconSize() );

  recent->saveEntries( config );

  kpackage->management->writeTreePos();
  kpackage->management->writeTreeType();

  keys->writeSettings();

  config->sync();
}

void KPKG::setOptions(){
  optiondialog->restore();
}

void KPKG::setKeys(){
  KKeyDialog::configureKeys( keys );
}

void KPKG::saveSettings(){
  writeSettings();
}

void KPKG::clearPCache(){
  cacheObj::clearPCache();
}

void KPKG::clearDCache(){
  cacheObj::clearDCache();
}

void KPKG::saveProperties(KConfig *config )
{
    config->writeEntry("Name", kpackage->save_url.url());
}


void KPKG::readProperties(KConfig *config)
{
    QString entry = config->readEntry("Name"); // no default
    if (entry.isNull())
	return;
    kpackage->openNetFiles(entry);
    prop_restart = true;
}

void KPKG::closeEvent ( QCloseEvent *) {
    kpackage->fileQuit();
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

KPACKAGE::KPACKAGE(KConfig *_config, QWidget *parent)
  : QWidget(parent)
{

  // Save copy of config
  config = _config;

  setAcceptDrops(true);
  setupModeWidgets();

  setupStatusBar();

  file_dialog = NULL;
  findialog = NULL;
  srchdialog = NULL;

}

// Destructor
KPACKAGE::~KPACKAGE()
{
  //  destroyModeWidgets();
  //  delete status;
  //  delete processProgress;
}

// resize event -- arrange the widgets
void KPACKAGE::resizeEvent(QResizeEvent *re)
{
  re = re;			// prevent warning
  arrangeWidgets();
}

// Set up the mode widgets
void KPACKAGE::setupModeWidgets()
{
  management = new managementWidget(this);

  for (int i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      kpinterface[i]->uninstallation = new pkgOptionsU(kpinterface[i]);
      kpinterface[i]->installation = new pkgOptionsI(kpinterface[i]);
    }
  }
}

// destroy the mode widgets
void KPACKAGE::destroyModeWidgets()
{
  //  delete management;
  //  for (int i = 0; i < kpinterfaceN; i++) {
  //    if (kpinterface[i]) {
  //      delete kpinterface[i]->installation;
  //      delete kpinterface[i]->uninstallation;
  //    }
  //  }
}


// Set up the status bar
void KPACKAGE::setupStatusBar()
{
  statusbar = new QFrame(this);
  statusbar->setFrameStyle(QFrame::Raised | QFrame::Panel);
  processProgress = new KProgress(100,statusbar);
  processProgress->setTextEnabled(FALSE);

  status = new QLabel(i18n("Management Mode"), statusbar);
}

// Arrange the widgets nicely
void KPACKAGE::arrangeWidgets()
{
  int i;

  statusbar->resize(width(),20);
  statusbar->move(0,height()-20);
  status->resize((statusbar->width() / 4) * 3, 16);
  status->move(2,2);
  processProgress->resize(statusbar->width() / 4 - 4, 16);
  processProgress->move((statusbar->width() / 4) * 3 + 3, 2);

  management->resize(width(),height() - 20);

  for (i = 0; i < kpinterfaceN; i++)
    if (kpinterface[i]) {
      kpinterface[i]->installation->resize(width(),height() - 20);
    }
}

void KPACKAGE::setup()
{
  management->collectData(1);
}

void KPACKAGE::fileQuit()		// file->quit selected from menu
{
  kpkg->writeSettings();
  if (opts->DCache >= Opts::SESSION) {
    cacheObj::clearDCache(); // clear dir caches if needed
  }
  if (opts->PCache >= Opts::SESSION) {
    cacheObj::clearPCache(); // clear package caches if needed
  }

  KApplication::exit(0);	// exit the application
}

void KPACKAGE::reload()
{
  kpackage->management->collectData(TRUE);
}

void KPACKAGE::fileOpen()		// file->quit selected from menu
{
    KFileDialog *box;
    //    pkgInterface *type;

    box = getFileDialog(i18n("Select Package"));

    box->exec();

    if (!box->result())   /* cancelled */
      return;
    if(box->selectedURL().isEmpty()) {  /* no selection */
      return;
    }

    openNetFile( box->selectedURL() );
}

void KPACKAGE::clearMarked()
{
  management->clearMarked(management->treeList->firstChild());
}

void KPACKAGE::expandTree()
{
  management->expandTree(management->treeList);
}

void KPACKAGE::collapseTree()
{
  management->collapseTree(management->treeList);
}

pkgInterface *KPACKAGE::pkType(QString fname)
{
  // Get the package information for this package
  char buf[51];
  int i;

  FILE *file= fopen(QFile::encodeName(fname),"r");
  if (file) {
    fgets(buf,sizeof(buf)-1,file);
    buf[50] = 0;

    // check enabled package handlers
    for (i = 0; i < kpinterfaceN; i++) {
      if (kpinterface[i]) {
	if (opts->handlePackage[i] && kpinterface[i]->isType(buf, fname)) {
	  fclose(file);
	  return kpinterface[i];
	}
      }
    }
    // check unenabled package handlers
    for (i = 0; i < kpinterfaceN; i++) {
      if (kpinterface[i]) {
	if (!opts->handlePackage[i] && kpinterface[i]->isType(buf, fname)) {
	  fclose(file);
	  return kpinterface[i];
	}
      }
    }
    fclose(file);
    KpMsgE(i18n("Unknown package type: %1").arg(fname),TRUE);
  } else {
    KpMsgE(i18n("File not found: %1").arg(fname),TRUE);
  }

  return 0;
}

/////////////////////////////////////////////////////////////////////////

void KPACKAGE::openNetFiles (QStringList urls, bool install )
{
  QStringList files;
  int i;
  QPtrList<packageInfo> **lst = new QPtrList<packageInfo>*[kpinterfaceN];
  packageInfo *pk = 0;

  kdDebug() << "openNetFiles\n";

  for (QStringList::Iterator it = urls.begin(); it != urls.end(); ++it) {
    files.append(fetchNetFile(*it));
    kpkg->add_recent_file(*it);
  }

  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      lst[i] = new QPtrList<packageInfo>;
      for (QStringList::Iterator t = files.begin(); t != files.end(); ++t) {
	pkgInterface *type = pkType(*t);
	if (type ==  kpinterface[i]) {
	  pk = type->getPackageInfo('u', *t, 0);
	  if (pk) {
	    pk->pkgFileIns(*t);
	    lst[i]->insert(0,pk);
	  }
	}
      }
    }
  }

  if (install)
    for (i = 0; i < kpinterfaceN; i++) {
      if (kpinterface[i]) {
	if ( lst[i]->count() > 0) {
	  kpinterface[i]->installation->setup(lst[i],kpinterface[i]->head);
	  if (kpinterface[i]->installation->exec()) {
	    for (packageInfo *inf = lst[i]->first(); inf != 0; inf = lst[i]->next()) {
	      kpackage->management->updatePackage(inf,TRUE);
	    }
	  }
	}
      }
    } else {
      if (pk) {
	KPLVItem *pt = pk->item;
	// NOT the best place for this CODE
	kpackage->management->tabChanged(Opts::ALL);
	if (pt)
	  kpackage->management->packageHighlighted(pt);
      }
    }

  // Dealloc memory
  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      if ( lst[i]->count() > 0) {
	delete lst[i];
      }
    }
  }
  delete [] lst;
}

void KPACKAGE::openNetFile(const KURL &url, bool install )
{
  openNetFiles(url.url(), install);
}

//    KMimeMagic *magic = KMimeMagic::self();
//    KMimeMagicResult *r = magic->findFileType(s);
    //    printf("r=%s\n",(r->mimeType()).data());



QString KPACKAGE::getFileName(const KURL & url, QString &cacheName )
{
  QString none  = "";
  QString fname = "";

  if ( url.isMalformed() )  {
    KpMsgE(i18n("Malformed URL: %1").arg(url.url()),TRUE);
  } else {

    // Just a usual file ?
    if ( url.isLocalFile() ) {
      cacheName = url.path();
      fname = url.path();
    } else {

      QString tmpd = cacheObj::PDir();
      if (!tmpd.isEmpty()) {

	QString cacheFile = tmpd + url.fileName();

	cacheName = cacheFile;
	QFileInfo f(cacheFile);
	if (f.exists() && (opts->DCache != Opts::NEVER)) {
	  fname =  cacheFile;
	}
      }
    }
  }
  return fname;
}

QString KPACKAGE::fetchNetFile( const KURL & url )
{

  QString cf;

  QString f = getFileName(url, cf);

  if (cf.isEmpty()) {
    return "";
  } else {

    if (!f.isEmpty()) {
      return f;
    } else {
      save_url = url;

      setStatus(i18n("Starting Kio"));

      Kio kio;

      if (kio.download(url, cf)) {
	setStatus(i18n("Kio finished"));
	QFileInfo f(cf);
	if (!(f.exists() && f.size() > 0)) {
	  unlink(QFile::encodeName(cf));
	  return "";
	} else {
	  return cf;
	}
      } else {
	setStatus(i18n("Kio failed"));
	return "";
      }
    }
  }
}

/////////////////////////////////////////////////////////////////////////
void KPACKAGE::fileOpenUrl(){

  KLineEditDlg geturl( i18n("Open Location:"), save_url.prettyURL(), this );

  if ( geturl.exec() )
    {
      kpkg->add_recent_file(geturl.text());
      openNetFile( geturl.text() );
    }
}

void KPACKAGE::find(){
  if (srchdialog)
    srchdialog->show();
  else
    srchdialog = new Search(this, "find package");
}

void KPACKAGE::findf(){
  if (findialog)
    findialog->show();
  else
    findialog = new FindF(this);
}

KFileDialog* KPACKAGE::getFileDialog(QString captiontext)
{

  if(!file_dialog) {
    file_dialog = new KFileDialog(QDir::currentDirPath(), "",
				  this,"file_dialog",TRUE);
  }

  QString pat;
  for (int i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i] && opts->handlePackage[i]) {
      pat += kpinterface[i]->packagePattern;
      pat += " ";
    }
  }
  file_dialog->setFilter(pat);
  file_dialog->setCaption(captiontext);
  //  file_dialog->rereadDir();

  return file_dialog;
}

void KPACKAGE::dragEnterEvent(QDragEnterEvent* e)
{
  e->accept(QUriDrag::canDecode(e));
}

void KPACKAGE::dropEvent(QDropEvent *de) // something has been dropped
{
  int i = 0;

  QStrList list;
  QUriDrag::decode(de, list);
  QStringList urls;

  for ( const char *s = list.first();
	s != 0;
	s = list.next(), i++ ) {
    urls.append(s);
  }
  openNetFiles(urls);
}

void KPACKAGE::setStatus(QString s)	// set the text in the status bar
{
  status->setText(s);
  kapp->processEvents();	// refresh the screen
}

QString KPACKAGE::getStatus()	// get the text in the status bar
{
  if(status)
    return status->text();
  else
    return "";
}

void KPACKAGE::setPercent(int x)	// set the progress in the status bar
{
  processProgress->setValue(x);
  kapp->processEvents();	// refresh it
}

//////////////////////////////////////////////////////////////////////////////



#include "kpackage.moc"
