#include "aptplugincontainer.h"

#include <QAction>
#include <qmessagebox.h>
#include <QMainWindow>

// NPlugin
#include <plugincontainer.h>
#include <iprogressobserver.h>
#include <iprovider.h>

// NUtil
#include <progressdisplaydlg.h>

// NApt
#include "dumpavailpackagedb.h"

// NApplication
#include "applicationfactory.h"
#include "runcommand.h"

// NPlugin
#include "aptpluginfactory.h"
#include "aptsearchplugin.h"
#include "aptactionplugin.h"
#include "packagestatusplugin.h"
#include "packagedescriptionplugin.h"
#include "installedversionplugin.h"
#include "availableversionplugin.h"

#include "aptsettingswidget.h"

#include <helpers.h>
#include <globals.h>



extern "C" 
{ 
	NPlugin::PluginContainer* new_aptplugin() 
	{
		return new NPlugin::AptPluginContainer;
	} 
	
	NPlugin::PluginInformation get_pluginInformation()
	{
		return NPlugin::PluginInformation("aptplugin", toString(NPackageSearch::VERSION), "Benjamin Mesing");
	} 
}

namespace NPlugin
{

/////////////////////////////////////////////////////
// Constructors/ Destructors
/////////////////////////////////////////////////////

AptPluginContainer::AptPluginContainer( )
  : BasePluginContainer()
{ 
	addPlugin("AptSearchPlugin");
	addPlugin("AptActionPlugin");
	addPlugin("PackageStatusPlugin");
	addPlugin("PackageDescriptionPlugin");
	addPlugin("InstalledVersionPlugin");
	addPlugin("AvailableVersionPlugin");
	_pAptSearchPlugin = 0;
	_pAptActionPlugin = 0;
	_pPackageStatusPlugin = 0;
	_pPackageDescriptionPlugin = 0;
	_pInstalledVersionPlugin = 0;
	_pAvailableVersionPlugin = 0;
	
	_pAptSearch = 0;
	_pPackageDB = 0;
	setInstallationTool(NApt::APT);
}

AptPluginContainer::~AptPluginContainer( )
{
	unloadAllPlugins();
	delete pluginFactory();
	delete _pAptSearch;
	// do not delete, _pAptSearch and _pPackageDB point to the same object
	// delete _pPackageDB;
}


/////////////////////////////////////////////////////
// Plugin Container Interface
/////////////////////////////////////////////////////
 
bool AptPluginContainer::init(IProvider* pProvider)
{
//	qDebug("Opening aptfront cache for packages");
//	aptFront::cache::Global::get().openPackages();
	
	NUtil::IProgressObserver* pObserver = pProvider->progressObserver();
	pObserver->setProgressRange(0, 97, true);
	NApt::AptFrontPackageDB* aptFrontPackageDB = new NApt::AptFrontPackageDB(pProvider);
	_pAptSearch = aptFrontPackageDB;
	_pPackageDB = aptFrontPackageDB;
	//_pPackageDB = new NApt::DumpAvailPackageDB(pObserver, pProvider->packages().size());
	BasePluginContainer::init(pProvider, new AptPluginFactory(pProvider, this, _pPackageDB, _pAptSearch));
	pObserver->setProgressRange(97, 98, true);
	_pAptSearchPlugin = dynamic_cast<AptSearchPlugin*>(requestPlugin("AptSearchPlugin"));
	_pAptActionPlugin = dynamic_cast<AptActionPlugin*>(requestPlugin("AptActionPlugin"));
	_pPackageStatusPlugin = dynamic_cast<PackageStatusPlugin*>(requestPlugin("PackageStatusPlugin"));
	pObserver->setProgressRange(98, 99, true);
	_pPackageDescriptionPlugin = dynamic_cast<PackageDescriptionPlugin*>(requestPlugin("PackageDescriptionPlugin"));
	_pInstalledVersionPlugin = dynamic_cast<InstalledVersionPlugin*>(requestPlugin("InstalledVersionPlugin"));
	_pAvailableVersionPlugin = dynamic_cast<AvailableVersionPlugin*>(requestPlugin("AvailableVersionPlugin"));
	pObserver->setProgressRange(99, 100, true);
	
	return true;
}

QWidget* AptPluginContainer::getSettingsWidget(QWidget* pParent)
{
	_pSettingsWidget = new AptSettingsWidget(pParent);
	_pSettingsWidget->setInstallationTool(_installationTool);
 	return _pSettingsWidget;
}

void AptPluginContainer::applySettings()
{
	setInstallationTool(_pSettingsWidget->installationTool());
}



/////////////////////////////////////////////////////
// BasePluginContainer Interface
/////////////////////////////////////////////////////


QDomElement AptPluginContainer::loadContainerSettings(const QDomElement source)
{
	if (source.tagName() != "ContainerSettings")
		return source;
	float settingsVersion;
	NXml::getAttribute(source, settingsVersion, "settingsVersion", 0.0f);
	int tool;
	NXml::getAttribute(source, tool, "installationTool", (int) 0);
	setInstallationTool((NApt::InstallationTool) tool);
	return NXml::getNextElement(source);
}

void AptPluginContainer::saveContainerSettings(NXml::XmlData& outData, QDomElement parent) const
{
	QDomElement containerElement = outData.addElement(parent, "ContainerSettings");
	outData.addAttribute(containerElement, 0.1f, "settingsVersion");
	outData.addAttribute(containerElement, _installationTool, "installationTool");
}




/////////////////////////////////////////////////////
// IAptMediator Interface
/////////////////////////////////////////////////////

QStringList AptPluginContainer::searchPatterns()
{
	if (!_pAptSearchPlugin)
		return QStringList();
	return _pAptSearchPlugin->searchPatterns();
}
	
void AptPluginContainer::setInstallationTool(NApt::InstallationTool tool) 
{
	_installationTool = tool;
}

QString AptPluginContainer::installationToolCommand() 
{
	return NApt::getInstallationToolCommand(_installationTool);
}


void AptPluginContainer::updateAptDatabase() 
{
	_pAptActionPlugin->qAptUpdateAction()->setEnabled(false);
	// this will fetch us the update of the db
	_pCommand = NApplication::ApplicationFactory::getInstance()->getRunCommand("AptUpdateProcess");
	connect(_pCommand, SIGNAL(quit()), SLOT(onAptUpdateFinished()) );
	
 	QString shell = "/bin/sh";
 	QString arg1 = "-c";
	// this is really ugly, I need to protect this argument, so it is not interpreted
	// as multiple arguments
	// first apt-get update is run and then the debtags index is updated, since the
	// apt database also contains information relevant for the debtags database
	QString arg2 = "'echo Updateing apt database && " + installationToolCommand() + " update && echo Updateing debtags index && /usr/bin/debtags --local update'";
 	_pCommand->addArgument(shell);
 	_pCommand->addArgument(arg1);
	_pCommand->addArgument(arg2);
	_pCommand->setTitle("Updateing apt database");

	try 
	{
		if ( !_pCommand->startAsRoot() )
		{
			provider()->reportError( tr("Command not executed"), tr("For an unknwon reason, the command could "
				"not be executed.") );
			delete _pCommand;
			_pCommand = 0;
			_pAptActionPlugin->qAptUpdateAction()->setEnabled(true);
		}
	}
	catch (const NException::RuntimeException& e)
	{
		provider()->reportError(tr("Command not executed"), toQString(e.description()));
		delete _pCommand;
		_pCommand = 0;
		_pAptActionPlugin->qAptUpdateAction()->setEnabled(true);
	}
} 


void AptPluginContainer::reloadAptDatabase()
{
	_pAptSearch->reloadPackageInformation(0);
	_pPackageDB->reloadPackageInformation(0);
}



/////////////////////////////////////////////////////
// AptPluginContainer functions
/////////////////////////////////////////////////////


void AptPluginContainer::onAptUpdateFinished()
{
	if (_pCommand->processExitedSuccessful())
	{
 		reloadAptDatabase();
	}
	delete _pCommand;
	_pCommand = 0;
	_pAptActionPlugin->qAptUpdateAction()->setEnabled(true);
	
	
// Mornfalls update code via apt-front
// [16:36] <mornfall> ben_vos: check aptFront::Manager
// [16:36] <mornfall> ben_vos: specifically update() method
// [16:37] <mornfall> ben_vos: my update code looks like this: http://rafb.net/paste/results/DQKiGJ84.html
// [16:38] <mornfall> ben_vos: just note that it invalidates any entities you may be holding
// [16:38] <mornfall> ben_vos: (stable entities will survive the reopen though)
// [16:38] <mornfall> stable entities are a lot more expensive though, so don't overdo it with those
// [16:39] <ben_vos> Cool, thanks, I'll take a look. 
// [16:39] <mornfall> i also think there are only package/version stable entities implemented


/*
void TestApp::update() {
    closeModes();
    setActionsEnabled( false );
    aptFront::Manager m;
    m.setProgressCallback( m_progress->callback() );
    m.setUpdateInterval( 100000 );
    try {
        m_stack->raiseWidget( m_progress );
        m.update();
    } catch ( exception::OperationCancelled ) { // ignore
    } catch (...) {
        KMessageBox::sorry( this,
                            i18n( "There was an error downloading updates. " ),
                            i18n( "Could not fetch updates" ) );
    }
    kdDebug() << "closing progress widget" << endl;
    m_stack->raiseWidget( m_list );
    setActionsEnabled( true );
    notifyPostChange( 0 );
}*/
	
}



}	// namespace NPlugin
