/*
 * unusedpackagesdialog.cc
 *
 * Copyright (c) 2002, 2003 Frerich Raabe <raabe@kde.org>
 *
 * 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. For licensing and distribution details, check the
 * accompanying file 'COPYING'.
 */
#include "unusedpackagesdialog.h"

#include <kapplication.h>
#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>
#if __FreeBSD__ == 5
#  include <kmessagebox.h>
#endif
#include <knotifyclient.h>

#include <qcheckbox.h>
#include <qfileinfo.h>
#include <qlabel.h>
#include <qgrid.h>
#include <qspinbox.h>
#include <qtimer.h>

using namespace Barry;

UnusedPackagesDialog::UnusedPackagesDialog( QWidget *parent, const char *name )
	: KDialogBase( parent, name, true, i18n( "Find unused packages" ),
	               User1 | Cancel, User1, true, i18n( "&Find" ) )
{
	QGrid *mainWidget = makeGridMainWidget( 2, Horizontal );

	new QLabel( i18n( "Days not accessed: " ), mainWidget );

	m_daysNotAccessed = new QSpinBox( 0, 365, 1, mainWidget, "m_daysNotAccessed" );

	KConfig *cfg = kapp->config();
	m_daysNotAccessed->setValue( cfg->readNumEntry( QString::fromLatin1( "Package files not accessed" ), 7 ) );

	m_leafPackagesOnly = new QCheckBox( i18n( "Show leaf packages only" ), mainWidget );
	m_leafPackagesOnly->setChecked( cfg->readBoolEntry( QString::fromLatin1( "Leaf packages only" ), true ) );
}

unsigned int UnusedPackagesDialog::daysNotAccessed() const
{
	return static_cast<unsigned int>( m_daysNotAccessed->value() );
}

bool UnusedPackagesDialog::leafPackagesOnly() const
{
	return m_leafPackagesOnly->isChecked();
}

void UnusedPackagesDialog::slotUser1()
{
	KConfig *cfg = kapp->config();
	cfg->writeEntry( QString::fromLatin1( "Package files not accessed" ), daysNotAccessed() );
	cfg->writeEntry( QString::fromLatin1( "Leaf packages only" ), leafPackagesOnly() );
	cfg->sync();

	accept();
}

UnusedPackagesProgressDialog::UnusedPackagesProgressDialog(
	unsigned int daysNotAccessed, bool leafPackagesOnly,
	QWidget *parent, const char *name )
	: ActionProgressDialog( parent, name ),
	m_daysNotAccessed( daysNotAccessed ),
	m_leafPackagesOnly( leafPackagesOnly ),
	m_currentDateTime( QDateTime::currentDateTime() )
{
	addColumn( i18n( "Name" ) );
	addColumn( i18n( "Version" ) );

#if __FreeBSD__ == 5
	const bool hasKernelBug = doKernelBugCheck();
	if ( !hasKernelBug && m_leafPackagesOnly )
#else
	if ( m_leafPackagesOnly )
#endif
		search( determineLeafPackages() );
	else
		search( PortMgr::self().installedPorts() );
}

/* We'd better point out that there is a known bug which keeps the atime
 * being updated after an exec(), so make sure the user knows that there
 * will be quite a lot packages listed which are not unused at all.
 */
#if __FreeBSD__ == 5
bool UnusedPackagesProgressDialog::doKernelBugCheck()
{
	return KMessageBox::warningContinueCancel( this,
		i18n( "<qt>You seem to be running FreeBSD 5, which is likely to have a "
		      "bug in the kernel which keeps the date of last access from being "
		      "set correctly when a program file gets executed.<br/><br/>"
		      "This means that the list of unused packages is likely to "
		      "contain packages which aren't unused at all, so you need to "
		      "check very carefully before removing seemingly unused "
		      "packages! Are you sure you want to go on?</qt>" ),
		      i18n( "Possible kernel bug detected" ) ) == KMessageBox::Cancel;
}
#endif

PortList UnusedPackagesProgressDialog::determineLeafPackages()
{
	PortList leaves;
	const PortList installed = PortMgr::self().installedPorts();
	PortList::ConstIterator it = installed.begin();
	PortList::ConstIterator end = installed.end();
	for ( ; it != end; ++it ) {
		Port port = *it;

		if ( port.isNull() )
			continue;
		const QString dirName = QString( QString::fromLatin1( "%1/%2-%3" ) )
		                        .arg( PortMgr::self().pkgRootDir() )
		                        .arg( port.name() )
		                        .arg( port.installedVersion() );

		if ( !QFile::exists( dirName ) ) {
			kdWarning() << "Directory " << dirName << " doesn't exist!" << endl;
			continue;
		}

		if ( !QFile::exists( dirName + QString::fromLatin1( "/+REQUIRED_BY" ) ) )
			leaves += port;
	}
	return leaves;
}

void UnusedPackagesProgressDialog::processPort( const Port &port )
{
	if ( port.isNull() )
		return;

	bool unused = true;
	const QStringList files = port.installed() ? port.installedFiles() : port.files();
	QStringList::ConstIterator it = files.begin();
	QStringList::ConstIterator end = files.end();
	for ( ; it != end; ++it ) {
		if ( !QFile::exists( *it ) ) {
			KNotifyClient::event( QString::fromLatin1( "NoPortFile" ),
				i18n( "The file %1 listed in the the file list of port %2-%3 "
				      "doesn't exist!" )
				      .arg( *it ).arg( port.name() ).arg( port.version() ) );
			unused = false;
			break;
		}
		const QFileInfo fi( *it );
		if ( fi.lastRead().daysTo( m_currentDateTime ) < m_daysNotAccessed ) {
			unused = false;
			break;
		}
	}

	if ( unused )
		enlistPort( port );
}

#include "unusedpackagesdialog.moc"
// vim:ts=4:sw=4:noet:list
