///////////////////////////////////////////////////////////////////////////
// $Id: managementWidget.cpp,v 1.60 2002/09/11 15:04:25 toivo Exp $
// File  : managementWidget.cpp
// Author: Damyan Pepper
// Author: Toivo Pedaste
//
// See managementWidget.h for more information

#include <qsplitter.h>
#include <qptrstack.h>

#include <klocale.h>

// kpackage.headers
#include "kpackage.h"
#include "kplview.h"
#include "managementWidget.h"
#include "pkgInterface.h"
#include "pkgOptions.h"
#include "packageDisplay.h"
#include "packageProperties.h"
#include "options.h"

extern Opts *opts;

// constructor -- initialise variables
managementWidget::managementWidget(QWidget *parent)
  : QFrame(parent)
{
  install_action=0;
  uninstall_action=0;
  package=0;

  allPackages  = new QPtrList<packageInfo>;
  QPtrStack<QString> stack();

  tType[0] = i18n("Installed");
  tType[1] = i18n("Updated");
  tType[2] = i18n("New");
  tType[3] = i18n("All");

  dirInstPackages = new QDict<packageInfo>(7717);
  dirUninstPackages = new QDict<packageInfo>(7717);
  dirInfoPackages = new QDict<packageInfo>(7717);
  setupWidgets();
}

managementWidget::~managementWidget()
{
  //  if(allPackages)
  //      delete allPackages;
  //  delete dirInstPackages;
  //  delete dirUninstPackages;
}

void managementWidget::resizeEvent(QResizeEvent *)
{
  arrangeWidgets();
}


void managementWidget::setupWidgets()
{
  QTab t;

  top = new QBoxLayout(this,QBoxLayout::TopToBottom);
  vPan  = new QSplitter(QSplitter::Horizontal, this);
  top->addWidget(vPan);

  // the left panel
  leftpanel = new QFrame(vPan);
  leftbox = new QBoxLayout(leftpanel,QBoxLayout::TopToBottom);

  QTabBar *ltab = new QTabBar(leftpanel);

  readTreeType();
  treeList = new KPQListView(leftpanel);

  treeList->setFrameStyle(QFrame::Panel|QFrame::Sunken);
  treeList->setLineWidth(2);
  treeList->setItemMargin(2);
  treeList->addColumn(i18n("Package"));
  treeList->setColumnWidthMode(0,QListView::Manual);
  treeList->addColumn(i18n("Mark"));
  treeList->setColumnWidthMode(1,QListView::Manual);
  treeList->addColumn(i18n("Size"));
  treeList->setColumnWidthMode(2,QListView::Manual);
  treeList->addColumn(i18n("Version"));
  treeList->setColumnWidthMode(3,QListView::Manual);
  treeList->addColumn(i18n("Old Version"));
  treeList->setColumnWidthMode(4,QListView::Manual);
  //  treeList->setAllColumnsShowFocus(TRUE);
  treeList->setRootIsDecorated(TRUE);
  readTreePos();
  treeList->update();
  treeList->show();

  for (int i = 0; i < 4; i++) {
    QTab *t = new QTab();
    t->setText( tType[i] );
    ltab->addTab(t);
  }
  connect(ltab,SIGNAL(selected (int)),SLOT(tabChanged(int)));
  ltab->setCurrentTab(treeType);


  //  ltab->tabBar.setCurrentTab(treeType);
  leftbox->addWidget(ltab,10);
  leftbox->addWidget(treeList,10);

  leftbox->addStretch();

  lbuttons = new QBoxLayout(QBoxLayout::LeftToRight);

  luinstButton = new QPushButton(i18n("Uninstall Marked"),leftpanel);
  luinstButton->setEnabled(TRUE);
  connect(luinstButton,SIGNAL(clicked()),
	  SLOT(uninstallMultClicked()));
  linstButton = new QPushButton(i18n("Install Marked"),leftpanel);
  linstButton->setEnabled(TRUE);
  connect(linstButton,SIGNAL(clicked()),
	  SLOT(installMultClicked()));

  leftbox->addLayout(lbuttons,0); // top level layout as child

  // Setup the `buttons' layout
  lbuttons->addWidget(linstButton,1,AlignBottom);
  lbuttons->addWidget(luinstButton,1,AlignBottom);
  lbuttons->addStretch(1);

  connect(treeList, SIGNAL(selectionChanged(QListViewItem *)),
	  SLOT(packageHighlighted(QListViewItem *)));

  // the right panel
  rightpanel = new QFrame(vPan);
  rightbox = new QBoxLayout(rightpanel,QBoxLayout::TopToBottom);

  packageDisplay = new packageDisplayWidget(rightpanel);
  //  packageDisplay->setFrameStyle(QFrame::Panel|QFrame::Sunken);
  //  packageDisplay->setLineWidth(2);
  connect(this, SIGNAL(changePackage(packageInfo *)),
	  packageDisplay, SLOT(changePackage(packageInfo *)));

  rbuttons = new QBoxLayout(QBoxLayout::LeftToRight);

  uinstButton = new QPushButton(i18n("Uninstall"),rightpanel);
  uinstButton->setEnabled(FALSE);
  connect(uinstButton,SIGNAL(clicked()),
	  SLOT(uninstallSingleClicked()));
  instButton = new QPushButton(i18n("Install"),rightpanel);
  instButton->setEnabled(FALSE);
  connect(instButton,SIGNAL(clicked()),
	  SLOT(installSingleClicked()));


  // Setup the `right panel' layout
  rightbox->addWidget(packageDisplay,10);
  rightbox->addLayout(rbuttons,0); // top level layout as child

  // Setup the `buttons' layout
  rbuttons->addWidget(instButton,1);
  rbuttons->addWidget(uinstButton,1);
  rbuttons->addStretch(1);
}

////////////////////////////////////////////////////////////////
void managementWidget::writePSeparator()
{
  KConfig *config = kapp->config();

  config->setGroup("Kpackage");

  config->writeEntry("panel1Width",vPan->sizes().first());
  config->writeEntry("panel2Width",vPan->sizes().last());
}

void managementWidget::readPSeparator()
{
  KConfig *config = kapp->config();

  config->setGroup("Kpackage");

  int w1 = config->readNumEntry("panel1Width",200);
  int w2 = config->readNumEntry("panel2Width",200);

  QValueList<int> size;
  size << w1 << w2;
  vPan->setSizes(size);
}

////////////////////////////////////////////////////////////////
void managementWidget::writeTreeType()
{
  KConfig *config = kapp->config();

  config->setGroup("Kpackage");

  config->writeEntry("Package_Display",treeType);
}

void managementWidget::readTreeType()
{
  KConfig *config = kapp->config();

  config->setGroup("Kpackage");

  treeType = config->readNumEntry("Package_Display",3);

}

///////////////////////////////////////////////////////////////////
void managementWidget::setupInstButton()
{
  bool u,i;
  if (!package)
  {
    i = false;
    u = false;
  }
  else
  {
    if (package->interface->noFetch || !package->getFilename().isEmpty() ) {
      instButton->setText(i18n("Install"));
    } else {
      instButton->setText(i18n("Fetch"));
    }
    if (package->packageState != packageInfo::INSTALLED &&
        !package->getProperty("filename").isNull() ) {

      i = true;
      u = false;
    } else {
      i = false;
      u = true;
    }
  }
  instButton->setEnabled(i);
  if (install_action)
    install_action->setEnabled(i);

  uinstButton->setEnabled(u);
  if (uninstall_action)
     uninstall_action->setEnabled(u);
}

void managementWidget::arrangeWidgets()
{
  // this is done automatically by the layout managers
}

void managementWidget::tabChanged(int tab)
{
  treeType = tab;
  sweep();
}


// Collect data from package.
void managementWidget::collectData(bool refresh)
{
  int i;

  if (!refresh && allPackages) {
    sweep();
    return; // if refresh not required already initialised
  }

  QApplication::setOverrideCursor( waitCursor );

// stop clear() sending selectionChanged signal
  disconnect(treeList, SIGNAL(selectionChanged(QListViewItem *)),
	  this, SLOT(packageHighlighted(QListViewItem *)));
  treeList->hide();    // hide list tree
  treeList->clear();   // empty it
  connect(treeList, SIGNAL(selectionChanged(QListViewItem *)),
	  SLOT(packageHighlighted(QListViewItem *)));

  packageDisplay->noPackage();

  // Delete old list if necessary
  if(allPackages) {
    delete allPackages;
  }

  allPackages = new QPtrList<packageInfo>;
  allPackages->setAutoDelete(TRUE);

  dirInstPackages->clear();
  dirUninstPackages->clear();
  // List installed packages
  for (i = 0; i < kpinterfaceN; i++)  {
    if (kpinterface[i] && opts->handlePackage[i]) {
      if (hostName.isEmpty() || (kpinterface[i]->hasRemote)) {
	kpinterface[i]->listPackages(allPackages);
      }
    }
  }

  // Rebuild the list tree
  rebuildListTree();

  QApplication::restoreOverrideCursor();
}

// Rebuild the list tree
void managementWidget::rebuildListTree()
{
  packageInfo *i;
  int  n = 0;

  kpackage->setStatus(i18n("Building package tree"));
  kpackage->setPercent(0);

  treeList->setSorting(-1);

  // place all the packages found
  int count = allPackages->count();
  int incr = count/50;
  if (incr == 0)
    incr = 1;

  for(i=allPackages->first(); i!=0; i=allPackages->next())
    {
      i->place(treeList,TRUE);

      if (!(n % incr)) {
	kpackage->setPercent(int (n*100/count));
      }
      n++;
    }
  treeList->show();		// show the list tree
  sweep();

  treeList->setSorting(0);

  kpackage->setPercent(100);	// set the progress
  kpackage->setStatus("");
}

// A package has been highlighted in the list tree
void managementWidget::packageHighlighted(QListViewItem *item)
{

  KPLVItem *sel = (KPLVItem *)item;

  if (!sel || sel->childCount()) {
    emit changePackage(0);
    package = 0;
  } else if (sel) {

    if (!notPress) {
      int n = stack.at();
      int num = stack.count();
      for (int i = num - 1; i >= n; i--) {
	stack.remove(i);
      }
      kpkg->disableNext();
    }

    // Disable the tree list while we do this
    treeList->setEnabled(FALSE);
    // Tell everything that is interested to change packages
    emit changePackage(sel->info);

    // Keep copy of the package
    package = sel->info;

    // Re-enable the treeList and uninstall button
    treeList->setEnabled(TRUE);
    treeList->setFocus();

    if (!notPress) {
      stack.append(sel);
    }
  }
  setupInstButton();

  notPress = false;
  // Update the status bar
  kpackage->setPercent(100);
}

/////////////////////////////////////////////////////////////////////////
// install has been clicked

void managementWidget::installSingleClicked()
{
  int result;
  QPtrList<packageInfo> plist;

  if (package) {
    QString filename = package->getFilename();
    kdDebug() << "File=" << filename << "\n";
    pkgInterface *interface = package->interface;
    if (interface->noFetch || !filename.isEmpty()) {
      plist.append(package);
      interface->installation->setup(&plist, interface->head);
      result = interface->installation->exec();

      if(result == QDialog::Accepted) { // execute it
	// it was accepted, so the package has been installed
	packageInfo *inf;
	for (inf = plist.first(); inf != 0; inf = plist.next()) {
	  updatePackage(inf,TRUE);
	}

	emit changePackage(0); // change package to no package
	package = 0;
        setupInstButton();
      }
    } else {
      QString url = package->getUrl();
      if (!url.isEmpty()) {
	QString s = kpackage->fetchNetFile(url);
	if (!s.isEmpty()) {
	  emit changePackage(package);
	  setupInstButton();
	}
      } else {
	KpMsgE(i18n("Filename not available\n"),TRUE);
      }
    }
  }
  kpackage->setPercent(100);
}

// install has been clicked
void managementWidget::installMultClicked()
{
  int  i;
  KPLVItem *it;
  packageInfo *inf;
  QPtrList<packageInfo> **lst = new QPtrList<packageInfo>*[kpinterfaceN];

  selList.clear();
  findMarked(treeList->firstChild());
  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      lst[i] = new QPtrList<packageInfo>;
      for (it = selList.first(); it != 0; it = selList.next()) {
	if (it->info->interface == kpinterface[i] &&
	    it->childCount() == 0 &&
	    (it->info->packageState == packageInfo::UPDATED ||
	     it->info->packageState == packageInfo::NEW)
	    ) {
	  lst[i]->insert(0,it->info);
	}
      }
    }
  }
  selList.clear();

  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 (inf = lst[i]->first(); inf != 0; inf = lst[i]->next()) {
	    updatePackage(inf,TRUE);
	  }
	}
	delete lst[i];
      }
    }
  }
  delete [] lst;
}

/////////////////////////////////////////////////////////////////////////////
// Uninstall has been clicked

void managementWidget::uninstallSingleClicked()
{
  int result;
  QPtrList<packageInfo> plist;

  if (package) {			// check that there is a package to uninstall
    pkgInterface *interface = package->interface;
    plist.append(package);
    interface->uninstallation->setup(&plist, interface->head);
    result = interface->uninstallation->exec();

    if(result == QDialog::Accepted) { // execute it
      // it was accepted, so the package has been uninstalled
      packageInfo *inf;
      for (inf = plist.first(); inf != 0; inf = plist.next()) {
	updatePackage(inf,FALSE);
      }

      emit changePackage(0); // change package to no package
      package = 0;
      setupInstButton();
    }
  }
  kpackage->setPercent(100);
}

void managementWidget::uninstallMultClicked()
{
  int  i;
  KPLVItem *it;
  packageInfo *inf;
  QPtrList<packageInfo> **lst = new QPtrList<packageInfo>*[kpinterfaceN];

  selList.clear();
  findMarked(treeList->firstChild());
  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      lst[i] = new QPtrList<packageInfo>;
      for (it = selList.first(); it != 0; it = selList.next()) {
	if (it->info->interface == kpinterface[i] &&
	    it->childCount() == 0 &&
	    (it->info->packageState == packageInfo::INSTALLED ||
	     it->info->packageState == packageInfo::BAD_INSTALL)
	    ) {
	  lst[i]->insert(0,it->info);
	}
      }
    }
  }
  selList.clear();

  for (i = 0; i < kpinterfaceN; i++) {
    if (kpinterface[i]) {
      if (lst[i]->count() > 0) {
	kpinterface[i]->uninstallation->setup(lst[i],kpinterface[i]->head);
	if (kpinterface[i]->uninstallation->exec()) {
	  for (inf = lst[i]->first(); inf != 0; inf = lst[i]->next()) {
	    updatePackage(inf,FALSE);
	  }
	  delete lst[i];
	}
      }
    }
  }
  delete [] lst;
}

///////////////////////////////////////////////////////////////////////////
void managementWidget::sweep()
{
  KPLVItem  *item = treeList->firstChild();

  if (!item)
    return;

  do {
      sweepChild(item->firstChild());
  } while ((item = item->nextSibling()));
}

int  managementWidget::sweepChild(KPLVItem *it)
{
  int ret, shown = 0;

  do {
    if (it->childCount() > 0) {
      ret = sweepChild(it->firstChild());
      if (!ret) {
	it->hide();
      } else {
	it->show();
      }
      shown += ret;
    } else {
      //      printf("S=%s d=%d\n",(it->info->getProperty("name")).ascii(),
      //      it->info->display(treeType));
      if (!it->info->display(treeType)) {
	it->hide();
      } else {
	it->show();
	shown++;
      }
    }
  } while ((it = it->nextSibling()));
  return shown;
}

///////////////////////////////////////////////////////////////////////////
KPLVItem *managementWidget::search(QString str, QString head,
				KPLVItem  *start)
{
  KPLVItem  *item = treeList->firstChild();

  searchCitem = start;
  searchSkip = FALSE;
  searchSubstr = FALSE;
  searchStr = str;
  searchResult = 0;

  do {
    if (item->text(0) == head) {
      searchChild(item->firstChild());
      if (searchResult != 0)
	return searchResult;
    }
  } while ((item = item->nextSibling()));
  return 0;
}

KPLVItem *managementWidget::search(QString str, bool subStr, bool wrap,
			     bool start=FALSE)
{
  if (!treeList->firstChild())
    return 0;

  if (start)
    searchCitem = 0;
  else
    searchCitem = treeList->currentItem();
  searchSkip = !wrap;
  searchSubstr = subStr;
  searchStr = str;
  searchResult = 0;

  searchChild(treeList->firstChild());

  return changePack(searchResult);
}

bool managementWidget::searchChild(KPLVItem *it)
{
  do {
    if (!searchSkip) {
      QString s = it->text(0);
      //      kdDebug() << "s='" << s << "'='" <<  searchStr << "\n";
      if ((it->childCount() == 0) && (it->info->display(treeType)) &&
	  (searchSubstr ? s.contains(searchStr,FALSE) : s == searchStr)) {
	searchResult = it;
	return TRUE;
      }
    }

    if (searchCitem == it) {
      if (searchSkip) {
	searchSkip = FALSE;
      } else {
	return TRUE;
      }
    }

    if (it->childCount() > 0) {
      if (searchChild(it->firstChild()))
	return TRUE;
    }
  } while ((it = it->nextSibling()));
  return FALSE;
}

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

void managementWidget::doChangePackage(packageInfo *p)
{
  emit changePackage(p);
}

///////////////////////////////////////////////////////////////////////////
void managementWidget::stackRemove(KPLVItem *pack)
{
  int n = stack.find(pack);
  if (n >= 0) {
    if (n == 0) {
      kpkg->disablePrevious();
    }
    if (n >= int(stack.count() - 1)) {
      kpkg->disableNext();
    }
    stack.remove(pack);
  }
}

KPLVItem *managementWidget::changePack(KPLVItem *searchResult, bool push)
{
  if (searchResult) {
    QListViewItem *i;

    i = searchResult;
    while ((i = i->parent())) {
      i->setOpen(TRUE);
    }
    if (push) {
      stack.append(searchResult);
      kpkg->enablePrevious();
    }

    notPress = true;
    treeList->setSelected(searchResult,TRUE);
    treeList->setCurrentItem(searchResult);
    treeList->ensureItemVisible(searchResult);
    return searchResult;
  } else {
    return 0;
  }
}

void managementWidget::next()
{
  int n = stack.at();
  KPLVItem *s = stack.at(n + 1);
  if (s) {
    changePack(s, false);
  }
  if (n >= int(stack.count() - 2)) {
    kpkg->disableNext();
  }
  if (n >= 0) {
    kpkg->enablePrevious();
  }
}

void managementWidget::previous()
{
  int n = stack.at();
  KPLVItem *s = stack.at(n-1);

  if (s) {
    changePack(s, false);
    kpkg->enableNext();
  }
  if (n <= 1) {
    kpkg->disablePrevious();
  }
  if (n < int(stack.count() - 2)) {
    kpkg->enableNext();
  }

}


///////////////////////////////////////////////////////////////////////////
KPLVItem *managementWidget::updatePackage(packageInfo *pki, bool install)
{
  QString version;
  KPLVItem *q;

  if (allPackages) {
    QString name(pki->getProperty("name"));
    if (pki->hasProperty("version"))
      version = pki->getProperty("version");
    else
      version = "";
    pkgInterface *interface = pki->interface;
    packageInfo *pnew = interface->getPackageInfo('i', name, version);
    packageInfo *ptree;
    QString pkgId =  name + interface->typeID;

    if (install) {
      if (pnew) {
	if (pnew->packageState !=  packageInfo::BAD_INSTALL) {
	  ptree = dirInstPackages->find(pkgId); // remove installed entry
	  dirInstPackages->remove(pkgId);
	  if (ptree) {
	    if (ptree->getItem()) {
	      delete ptree->getItem();
	      ptree->item = 0;
	    }
	  }

	  ptree = dirUninstPackages->find(pkgId); // remove uninstalled entry
	  if (ptree) {
	    ptree->packageState = packageInfo::HIDDEN;
	    if (ptree->getItem()) {
	      delete ptree->getItem();
	      ptree->item = 0;
	    }
	  }
	}

	dirInstPackages->insert(pkgId,pnew);

	q = pnew->place(treeList,TRUE);
	allPackages->insert(0,pnew);
	if (!q) {
	  printf("NOTP=%s \n",pnew->getProperty("name").ascii());
	} else {
	  sweep();
	  return q;
	}
      }
    } else { // uninstalling
      if (!pnew) {
	dirInstPackages->remove(pkgId);
	KPLVItem  *qt = pki->getItem();
	if (qt) {
	  stackRemove(qt);
	  treeList->setSelected(qt,false);
	  if (treeList->markPkg == qt)
	    treeList->markPkg = 0;
	  delete qt;
	} else {
	  kdDebug() << "DEL=" << name << endl;
	}
	packageInfo *pb = dirUninstPackages->find(pkgId);
	if (pb) { // available package matching the one just uninstalled
	  pb->packageState = packageInfo::NEW;
	  q = pb->place(treeList,TRUE);
	  if (!q) {
	    printf("NOTP=%s \n",pb->getProperty("name").ascii());
	  } else {
	    sweep();
	    return q;
	  }
	}

      } else {
	delete pnew;
      }
    }
  }
  sweep();
  return 0;
}

///////////////////////////////////////////////////////////////////////////
void managementWidget::clearMarked(KPLVItem *item)
{
  while (item) {
    if (item->childCount() > 0) {
      clearMarked(item->firstChild());
    }
    item->setMark(false);
    item = item->nextSibling();
  }
}

void managementWidget::findMarked(KPLVItem *item)
{
  while (item) {
    if (item->childCount() > 0) {
      findMarked(item->firstChild());
    }
    if (item->marked) {
      selList.insert(0,item);
    }
    item = item->nextSibling();
  }
}

///////////////////////////////////////////////////////////////////////////
void managementWidget::expandTree(KPQListView *list)
{
 KPLVItem *item = list->firstChild();

  while (item) {
    if (item->childCount() > 0) {
      item->setOpen(TRUE);
      expandTree(item);
    }
  item = item->nextSibling();
  }
}


void managementWidget::expandTree(KPLVItem *pitem)
{
  KPLVItem *item = pitem->firstChild();

  while (item) {
    if (item->childCount() > 0) {
      item->setOpen(TRUE);
      expandTree(item);
    }
    item = item->nextSibling();
  }
}

void managementWidget::collapseTree(KPQListView *list)
{
  KPLVItem *item = list->firstChild();

  while (item) {
    if (item->childCount() > 0) {
      collapseTree(item);
    }
  item = item->nextSibling();
  }
}

void managementWidget::collapseTree(KPLVItem *pitem)
{
  int n = 0;
  KPLVItem *item = pitem->firstChild();

  while (item) {
    if (item->childCount() > 0) {
      n++;
      collapseTree(item);
    }
    item = item->nextSibling();
  };
  if (n)
    pitem->setOpen(TRUE);
  else
    pitem->setOpen(FALSE);
}

///////////////////////////////////////////////////////////////////////////
void managementWidget::writeTreePos()
{
  int i;

  KConfig *config = kapp->config();

  config->setGroup("Treelist");

  QString colpos;
  for (i = 0; i < 5; i++) {
    colpos.setNum(i);
    config->writeEntry(colpos,treeList->columnWidth(i));
  }
}

void managementWidget::readTreePos()
{
  int i, n;
  int num[] = {185,37,54,95,95};

  KConfig *config = kapp->config();

  config->setGroup("Treelist");

  QString colpos;
  for (i = 0; i < 5; i++) {
    colpos.setNum(i);
    n = config->readNumEntry(colpos,num[i]);
    treeList->setColumnWidth(i,n);
  }
}


#include "managementWidget.moc"
