
///////////////////////////////////////////////////////////////////////////////
// $Id: packageInfo.cpp,v 1.29.4.1 2002/12/13 15:20:22 toivo Exp $
// File  : packageInfo.cpp
// Author: Damyan Pepper
// Author: Toivo Pedaste
//
///////////////////////////////////////////////////////////////////////////////

#include "../config.h"
// Standard headers
#include <stdlib.h>

#include <qregexp.h>
#include <qdir.h>

// KDE headers
#include <kglobal.h>

// kpackage headers
#include "kpackage.h"
#include "kplview.h"
#include "packageInfo.h"
#include "pkgInterface.h"
#include "managementWidget.h"
#include "utils.h"
#include "options.h"
#include <klocale.h>
#include <ctype.h>

// Global pixmap for
QPixmap *pict = NULL;

//////////////////////////////////////////////////////////////////////////////
// Constructor -- get the pixmap
packageInfo::packageInfo(QDict<QString> *_info, pkgInterface *type)
{
  interface = type;
  info = _info;

  item = NULL;
  packageState = UNSET;
  updated = FALSE;
  url = QString::null;
}

// Another constructor, for a packge with a url
packageInfo::packageInfo(QDict<QString> *_info, QString _url)
{
  info = _info;
  url = _url;
  item = NULL;
}

packageInfo::~packageInfo()
{
  if (info)
    delete info;
}

// Return a property
QString packageInfo::getProperty(const QString &property)
{
  QString *result = info->find(property);
  if (!result) {
     return QString::null;
  }
  return *result;
}

// Check for existance of a property
bool packageInfo::hasProperty(const QString &property)
{
  QString *result = info->find(property);
  if (!result) 
     return false;
  return !result->isNull();
}

// Return the property dictionary
QDict<QString> *packageInfo::getDict()
{
  return info;
}

// Initialize fields if missing
void packageInfo::fixup()
{
  if (!info->find("group")) {
    info->insert("group", new QString(i18n("OTHER")));
  }

  if (!info->find("version")) {
    info->insert("version", new QString(""));
  }

  if (!info->find("name")) {
    QString *q = new QString("");;
    q->setNum((int)this);
    info->insert("name", q);
  }
}

// Set the file name
void packageInfo::setFilename(QString f)
{
  url = f;
}

// Get the url
QString packageInfo::getUrl()
{
  if (url.isEmpty()) {
    if (hasProperty("base") && hasProperty("filename"))
    {
      url = getProperty("base") + "/" + getProperty("filename");
    }
  }
  return url;
}

QString packageInfo::fetchFilename()
{
  QString f = getFilename();

  if (!f.isEmpty()) {
    return f;
  } else {
    QString aurl = getUrl();
    if (!aurl.isEmpty()) {
      return kpackage->fetchNetFile(aurl);
    } else {
      return getProperty("name");
    }
  }
}

QString packageInfo::getFilename()
{
  QString cn = "";
  QString aurl = getUrl();
  if (!aurl.isEmpty()) {
    return KPACKAGE::getFileName(aurl,cn);
  } else {
    return "";
  }
}

int packageInfo::getDigElement(QString s, int *pos)
  // Extract the next element from the string
  // All digits
{
  if (*pos < 0)
    return -1;

  s = s.mid(*pos);
  if (s.isEmpty())
    return -1;

  QRegExp ndig("[^0-9]");

  int nf = 0;
  int val = 0;

  if ((s[0] >= '0') && (s[0] <= '9')) {
    nf = s.find(ndig);
    if (nf >= 0) {
      val = s.left(nf).toInt();
    } else {
      val = s.toInt();
      nf = s.length();
    }
  }

  //  printf("n=%s %d %d\n",s.mid(nf,999).data(),nf,val);
  *pos += nf;
  return val;
}

QString packageInfo::getNdigElement(QString s, int *pos)
  // Extract the next element from the string
  // All  all non-digits
{
  if (*pos < 0)
    return QString::null;

  s = s.mid(*pos);
  if (s.isEmpty())
    return QString::null;

  QString str;
  int nf = 0;

  QRegExp idig("[0-9]");

  if ((s[0] < '0') || (s[0] > '9') ) {
    nf = s.find(idig);
    if (nf <  0)
      nf = s.length();
    str = s.left(nf);
    for (unsigned int i = 0; i < str.length() ; i++) {
      // Strange Debian package sorting magic
      if (!str[i].isLetter()) {
	char t = str[i].latin1();
	t += 128;
	str[i] = t;
      }
    }
  }
  *pos += nf;
  return str;
}


int packageInfo::pnewer(QString s, QString sp)
{
  int ns = 0, nsp = 0, vs, vsp;

  //  kdDebug() << "S=" << s  << " SP=" << sp  << "\n";
  while (TRUE) {
    vs = getDigElement(s,&ns);
    vsp = getDigElement(sp,&nsp);
    //    kdDebug() << "s=" << ns << " " << vs << " sp=" << nsp << " " << vsp << "\n";
    if (vs < 0 && vsp < 0)
      return 0;
    if (vs < 0 && vsp < 0)
      return 1;
    if (vs < 0 && vsp < 0)
      return -1;
    if (vsp > vs)
      return 1;
    else if (vs > vsp)
      return -1;

    QString svs = getNdigElement(s,&ns);
    QString svsp = getNdigElement(sp,&nsp);
    //    kdDebug() << "vs=" << ns << " " << svs << " sp=" << nsp << " " << svsp << "\n";
    if (svs == "" && svsp == "")
      return 0;
    if (svs == "" && svsp != "")
      return 1;
    if (svs != "" && svsp == "")
      return -1;

    if (svsp.isNull()) { // Allow for QT strangeness comparing null string
      svsp = "";
    }
    if (svs.isNull()) {
      svs = "";
    }
    int n = svsp.compare(svs);
    //    kdDebug() << "svsp=" << svsp << "=" << svsp.length() <<  " svs=" << svs << "=" <<svs.length() <<  " n=" << n << "\n";
    if (n != 0)
      return n;
  }
}

static bool split(QString orig, char seperator, QString &first, QString &second)
{
  int pos = orig.find(seperator);
  if (pos > 0) {
    first = orig.mid(0,pos);
    second = orig.mid(pos+1);
    return true;
  }
  return false;
}

int packageInfo::newer(packageInfo *p)
{
  QString mySerial;  // Serial number of this package
  QString myVersion; // Version of this package
  QString myRelease; // Release of this package

// Version of this package
  QString s = getProperty("version");

  (void) split(s, ':', mySerial, s);
  if (!split(s, '-', myVersion, myRelease))
  {
    myVersion = s;
  }

// Version of other package
  QString hisSerial;  // Serial number of the other package
  QString hisVersion; // Version of the other package
  QString hisRelease; // Release of the other package

  s = p->getProperty("version");
  if (p->hasProperty("release"))
  {
    s = s + "-" + p->getProperty("release");
  }
  if (p->hasProperty("serial"))
  {
    s = p->getProperty("serial") + ":" + s;
  }

  (void) split(s, ':', hisSerial, s);
  if (!split(s, '-', hisVersion, hisRelease))
  {
    hisVersion = s;
  }

  //  kdDebug() << "mySerial=" <<  mySerial << " hisSerial=" <<  hisSerial <<"\n";
  //  kdDebug() << "myVersion=" <<  myVersion << " hisVersion=" <<  hisVersion <<"\n";
  //  kdDebug() << "myRelease=" <<  myRelease << " hisRelease=" <<  hisRelease <<"\n";

  int n =  pnewer(mySerial,hisSerial);
  if (n)
    return n;
  else {
    n = pnewer(myVersion,hisVersion);
    if (n)
      return n;
    else
      return pnewer(myRelease,hisRelease);
  }
}

bool packageInfo::display(int treeType)
{
  switch (treeType) {
  case Opts::INSTALLED:
    if (packageState == INSTALLED || packageState == BAD_INSTALL)
      return TRUE;
    break;
  case Opts::UPDATED:
    if (packageState == UPDATED)
      return TRUE;
    break;
  case Opts::NEW:
    if  ((packageState == UPDATED) || (packageState == NEW))
      return TRUE;
    break;
  case Opts::ALL:
      return TRUE;
    break;
  };
  return FALSE;
}

//////////////////////////////////////////////////////////////////////
// Place the package in a QListView

KPLVItem *packageInfo::place(KPQListView *tree, bool insertI)
{
  KPLVItem *search = tree->firstChild(), *parent=NULL, *child=NULL;
  QString qtmp, tmp;
  bool doit = FALSE;

  doit = TRUE;
  if (packageState == NOLIST || packageState == HIDDEN)
    doit = FALSE;

  if (doit) {
    qtmp = interface->head;
    qtmp += "/";
    qtmp += getProperty("group");
    int cnt = 0;

    QStringList list = QStringList::split("/",qtmp);
    for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
      KPLVItem *group;

      if( search && (group=findGroup(*it, search)) ) {
	parent = group;
	parent->setOpen(TRUE);
	search = group->firstChild();
      } else {
	if (parent) {
	  group = new KPLVItem(parent, this, interface->folder, *it);
	} else {
	  group = new KPLVItem(tree, this, interface->folder, *it);
	}
	parent = group;
	parent->setOpen(TRUE);
	search = NULL;
      }
      cnt++;
    }

    tmp = *info->find("name");

    if(item)
      delete item;

    QString sz = "";
    if (info->find("size")) {
      sz = info->find("size")->stripWhiteSpace();
      if (sz.length() > 3)
	sz.truncate(sz.length() - 3);
      else
	sz = "0";
      sz += "K";
    } else if (info->find("file-size")) {
      sz = info->find("file-size")->stripWhiteSpace();
      if (sz.length() > 3)
	sz.truncate(sz.length() - 3);
      else
	sz = "0";
      sz += "k";
    }
    sz = sz.rightJustify(6,' ');

    QString ver = "";
    if (info->find("version")) {
      ver = *info->find("version");
    }

    QString over = "";
    if (info->find("old-version")) {
      over = *info->find("old-version");
    }

    QPixmap pic;
    if (packageState == BAD_INSTALL) {
      pic = interface->bad_pict;
    } else if (packageState == UPDATED) {
      pic = interface->updated_pict;
    } else if (packageState == NEW) {
      pic = interface->new_pict;
    } else if (packageState == INSTALLED) {
      pic = interface->pict;
    } else {
      pic = interface->pict;
    }

    if (child) {
      item =  new KPLVItem(child, this, pic, tmp, "", sz, ver, over);
    } else {
      item = new KPLVItem(parent, this, pic, tmp, "", sz, ver, over);
    }

    if (insertI) {
       parent->setOpen(TRUE);
    } else {
       parent->setOpen(FALSE);
    }

    return item;
  } else {
    return 0;
  }
}

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

// Get the QListViewItem
KPLVItem *packageInfo::getItem()
{
  return item;
}

//////////////////////////////////////////////////////////////////////////////
bool packageInfo::smerge( const QString &exp) {

  QDict<packageInfo> *dirInfoPackages = kpackage->management->dirInfoPackages;
  QString pname = getProperty("name") + exp;

  packageInfo *pi = dirInfoPackages->find(pname);
  if (pi) {
    QDictIterator<QString> it( *(pi->info) );

    while ( it.current() ) {
      if (!(it.currentKey() == "size" && info->find("size")) ||
	  !(it.currentKey() == "file-size"  && info->find("file-size"))) {
	info->insert(it.currentKey(), new QString(*it.current()));
      }
      ++it;
    }
    return TRUE;
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
void packageInfo::pkgFileIns(QString fileName)
{
  info->insert("filename", new QString(fileName));
  info->insert("base", new QString(""));

  if (pkgInsert(kpackage->management->allPackages, interface->typeID, FALSE)) {
    packageState = packageInfo::NEW;
    place(kpackage->management->treeList,TRUE);

    QString pname = getProperty("name") + interface->typeID;
    kpackage->management->dirUninstPackages->insert(pname,this);
  }
}

//////////////////////////////////////////////////////////////////////////////
bool packageInfo::pkgInsert(QPtrList<packageInfo> *pki, const QString &exp,
			 bool installed, bool infoPackage)
{
  QDict<packageInfo> *dirInstPackages = kpackage->management->dirInstPackages;
  QDict<packageInfo> *dirUninstPackages = kpackage->management->dirUninstPackages;
  QDict<packageInfo> *dirInfoPackages = kpackage->management->dirInfoPackages;

  QString pname = getProperty("name") + exp;
  //  printf("U1=%s\n",pname.data());

  bool shouldUpdate = TRUE;
  bool hidden = FALSE;

  packageInfo *pi = dirInstPackages->find(pname);
  if (pi) { // installed version exists
    if ((pi->packageState != BAD_INSTALL)
	&& (pi->packageState != NOLIST))  {
      if (newer(pi) >= 0) {
	hidden = TRUE;
      }
    }
  }

  packageInfo *pu = dirUninstPackages->find(pname);
  if (pu) { // available version exists
    if ((pu->packageState != BAD_INSTALL)
	&& (pu->packageState != NOLIST))  {
      if (newer(pu) >= 0) {
	shouldUpdate = FALSE;
      } else if (!installed) { // If older available package exists, remove it
	dirUninstPackages->remove(*(pu->info->find("name")));
	pki->remove(pu);
      }
    }
  }

  if (getProperty("version").isEmpty()) {
    shouldUpdate = TRUE;
  }

  if (shouldUpdate) {
    if (packageState != BAD_INSTALL) {
      if (installed)
	packageState = INSTALLED;
      else if (pi) { // installed version exists
	if (hidden) {
	  packageState = HIDDEN;
	} else {
	  QString version = pi->getProperty("version");
	  if (version.isEmpty()) {
	    if (pi->packageState == NOLIST)
	      packageState = NEW;
	    else
	      packageState = UPDATED;
	  } else {
	    packageState = UPDATED;
	    if (pi->hasProperty("old-version")) {
	      info->insert("old-version",
			   new QString(pi->getProperty("old-version")));
	    } else {
	      info->insert("old-version",new QString(version));
	    }
	    QString group = getProperty("group");
	    if (group == "NEW") {
	      if (pi->hasProperty("group")) {
		info->replace("group",
			      new QString(pi->getProperty("group")) );
	      }
	    }
	  }
	}
      } else
	packageState = NEW;
    }

    pki->insert(0,this);
    if (installed) {
      if (infoPackage)
	dirInfoPackages->insert(pname,this);
      else
	dirInstPackages->insert(pname,this);
    } else
      dirUninstPackages->insert(pname,this);
    return TRUE;
  } else {
    return FALSE;
  }
}

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

