/*
 * This file is part of DesktopBSD, see the README file.
 *
 * Authors: Peter Hofer <ph@desktopbsd.net>
 *			 Reinhard Pointner
 *			 (C) 2004, 2005
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */

#include "package.h"

#include <dirent.h>

#include <qfile.h>
#include <qregexp.h>

#include <pkgversion.h>
#include <executor.h>
#include <pkgconstants.h>

Package::Package() {
	instStatus = Undefined;
	installedSizeSet = false;
	longDescrSet = false;
}

/** This method expects *this to be the newest available
  * package (e.g. an object created from an INDEX 5 line)
  * and the parameter to be an object with the installed
  * package's information (from /var/db/pkg).
  */
void	Package::mergeInstalledPackage(const Package & pkg) {
	installedName = pkg.installedName;
	installedVersion = pkg.installedVersion;
	installedSizeSet = pkg.installedSizeSet;
	if(installedSizeSet)
		installedSize = pkg.installedSize;
	requiredBy = pkg.requiredBy;

	int status = version_cmp(getInstalledVersion(), getLatestVersion());
	instStatus = (status == -1) ? InstalledOutdated : ((status == 0) ? Installed : InstalledNewer);
}

/** Returns a list with all packages currently installed
  * on the system.
  */
QPtrList<Package>	Package::getInstalledPackages() {
	QPtrList<Package>	list;

	Package	*pkg;
	DIR		*dir = opendir(PKGDB_DIRECTORY);

	if(dir != NULL) {
		struct dirent	*ent;
		while((ent = readdir(dir)) != NULL)
			if(ent->d_type == DT_DIR  &&  ent->d_name[0] != '.') {
				pkg = new Package();
				pkg->instStatus = Installed;
				pkg->installedName = ent->d_name;			/* e.g. cups-base-1.1.23.0_3	*/

				/* Package origin not found in +CONTENTS */
				int versionpos = pkg->installedName.findRev('-', -2);
				pkg->installedVersion = pkg->installedName.mid(versionpos + 1);

				char  buffer[4096];
				char  fnbuffer[MAXNAMLEN] = { PKGDB_DIRECTORY };
				char  *end = stpcpy(fnbuffer + sizeof(PKGDB_DIRECTORY) - 1, ent->d_name);
				FILE  *fp;

#if 0
				/* Deactivate, it's not required and almost always
				 * merged with the package from the INDEX file 
				 */

				strcpy(end, "/+COMMENT");
				fp = fopen(fnbuffer, "r");
				if(fp != NULL) {
					if(fgets(buffer, sizeof buffer, fp) != NULL)
						pkg->shortDescr = buffer;

					fclose(fp);
				}
#endif

				strcpy(end, "/+CONTENTS");
				fp = fopen(fnbuffer, "r");
				if(fp != NULL) {
					/* Note that we can only get the main category
					 * (by reading the ORIGIN comment)
					 */
					const char origin[] = { "@comment ORIGIN:" };
					char  *originBgn = buffer + sizeof origin - 1;
					while(fgets(buffer, sizeof buffer, fp) != NULL) {
						if(strncmp(buffer, origin, sizeof origin - 1) == 0) {
							char  *slash = strchr(originBgn, '/');
							if(slash != NULL) {
								*slash = '\0';
								pkg->mainCategory = originBgn;

								/* Remove '\n' retained by fgets() */
								char  *newline = strchr(slash + 1, '\n');
								if(newline != NULL)
									*newline = '\0';

								pkg->name = slash + 1;
							}

							break;
						}
					}

					fclose(fp);
				}
				
				if(pkg->name.isEmpty())
					/* Origin not found in +CONTENTS */
					pkg->name = pkg->installedName.left(versionpos);

#if 0
				/* Deactivate, it's not required and almost always
				 * merged with the package from the INDEX file 
				 */

				strcpy(end, "/+DESC");
				fp = fopen(fnbuffer, "r");
				if(fp != NULL) {
					const char www[] = { "WWW: " };

					while(fgets(buffer, sizeof buffer, fp) != NULL)
						if(strncmp(buffer, www, sizeof www - 1) == 0) {
							char  *wwwStart = buffer + sizeof www - 1;
							while(*wwwStart == ' ')
								++wwwStart;

							if(*wwwStart != '\n'  &&  *wwwStart != '\0') {
								/* Remove '\n' retained by fgets() */
								char  *newline = strchr(buffer, '\n');
								if(newline != NULL)
									*newline = '\0';

								pkg->website = wwwStart;
							}

							break;
						}
					fclose(fp);
				}
#endif

				strcpy(end, "/+REQUIRED_BY");
				fp = fopen(fnbuffer, "r");
				if(fp != NULL) {
					while(fgets(buffer, sizeof buffer, fp) != NULL) {
						/* Remove '\n' retained by fgets() */
						char  *newline = strchr(buffer, '\n');
						if(newline != NULL)
							*newline = '\0';

						pkg->requiredBy.append(buffer);
					}

					fclose(fp);
				}

				list.append(pkg);
			}
			
		  closedir(dir);
	}

	return list;
}


/** Creates a "Package" object from an INDEX-5 line. The version
  * number from the file will be written into latestVersion.
  *
  * This function will not set requiredBy.
  */
Package  *Package::fromINDEXLine(const QString  & line) {
	 Package  *pkg = new Package();
	 
	 /* Assume this package is not installed - merging with
	  * a package object created in getInstalledPackages()
	  * sets this to the appropriate value anyway.
	  */
	 pkg->instStatus = NotInstalled;
	 
	 QStringList  splitLine = QStringList::split('|', line, true);

	 /* Example:
	desktopbsd-tools-1.1|/usr/ports/sysutils/desktopbsd-tools|/usr/local|Deskto
	pBSD tools for easy system configuration|/usr/ports/sysutils/desktopbsd-too
	ls/pkg-descr|ph@desktopbsd.net|sysutils||||/usr/ports/devel/qmake /usr/port
	s/x11-toolkits/qt33 /usr/ports/x11/kdebase3 /usr/ports/x11/kdelibs3|/usr/po
	rts/shells/bash /usr/ports/sysutils/fastest_cvsup /usr/ports/sysutils/portu
	pgrade /usr/ports/x11-toolkits/qt33 /usr/ports/x11/kdebase3 /usr/ports/x11/
	kdelibs3 /usr/ports/x11/xterm|http://desktopbsd.net
	  */

	 /*
	  * pkg->name = "desktopbsd-tools"
	  * pkg->latestVersion = "1.1"
	  */
	 pkg->name = splitLine[1].section('/', -1);
	 pkg->mainCategory = splitLine[1].section('/', -2, -2);
	 
	 int versionpos = splitLine[0].findRev('-', -2);
	 pkg->latestVersion = splitLine[0].mid(versionpos + 1);


	 /* "DesktopBSD tools for easy system configuration" */
	 pkg->shortDescr = splitLine[3];


	 /* (Not used in UI)
	  *
	  * ph@desktopbsd.net
	  *
	 pkg->mail = splitLine[5];

	  *
	  * { "sysutils" }
	  
	 pkg->categories = QStringList::split(' ', splitLine[6]);
	  */


	 /* "http://desktopbsd.net" */
	 pkg->website = splitLine[9];

	 return  pkg;
}


/** @return The "ID" of the package, category/name.
  *			e.g. "multimedia/mplayer"
  */
QString	Package::getID()	const {
	return  getMainCategory() + '/' + getName();
}


/** Returns TRUE if the installed size of the package is
  * set, FALSE if the package is not installed or
  * the size would be calculated if getInstalledSize()
  * is called.
  */
bool	Package::isInstalledSizeSet() {
	return installedSizeSet;
}


/** Returns the installed size of the package if it is
  * installed on the system, otherwise 0.
  */
Size	Package::getInstalledSize() {
	if(installedSizeSet)
		return installedSize;

	if(instStatus != Installed && instStatus != InstalledOutdated && instStatus != InstalledNewer)
		return Size();

	int  size = Executor::intFromCmd(QString("/usr/sbin/pkg_info -qs '%1'").arg(installedName));
	installedSize = Size(size);
	installedSizeSet = true;
	return installedSize;
}


bool  Package::isRequiredBy(const Package & pkg) {
	return requiredBy.contains(pkg.installedName);
}


QString Package::getLongDescr() {
	if(longDescrSet)
		return longDescr;

	QFile  f(PORTS_DIRECTORY + getID() + "/pkg-descr");
	if(f.open(IO_ReadOnly)) {
		QTextStream s(&f);
		longDescr = s.read();
		f.close();
	} else
		longDescr = "";

	longDescrSet = true;
	return longDescr;
}
