#include "aptplugincontainer.h"

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

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

// NUtil
#include <progressdisplaydlg.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>

// NApt
#include "aptpackagesearch.h"
#include "commandlinepackagedb.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;
    delete _pPackageDB;
}


/////////////////////////////////////////////////////
// Plugin Container Interface
/////////////////////////////////////////////////////
 
bool AptPluginContainer::init(IProvider* pProvider)
{
	NUtil::IProgressObserver* pObserver = pProvider->progressObserver();
    pObserver->setProgressRange(0, 94, true);
    _pPackageDB = new NApt::CommandLinePackageDB(*pProvider, *pObserver);
    _pAptSearch = new NApt::AptPackageSearch(_pPackageDB);
	BasePluginContainer::init(pProvider, new AptPluginFactory(pProvider, this, _pPackageDB, _pAptSearch));
    pObserver->setProgressRange(91, 94, true);
	_pAptSearchPlugin = dynamic_cast<AptSearchPlugin*>(requestPlugin("AptSearchPlugin"));
	_pAptActionPlugin = dynamic_cast<AptActionPlugin*>(requestPlugin("AptActionPlugin"));
	_pPackageStatusPlugin = dynamic_cast<PackageStatusPlugin*>(requestPlugin("PackageStatusPlugin"));
    pObserver->setProgressRange(94, 97, true);
	_pPackageDescriptionPlugin = dynamic_cast<PackageDescriptionPlugin*>(requestPlugin("PackageDescriptionPlugin"));
	_pInstalledVersionPlugin = dynamic_cast<InstalledVersionPlugin*>(requestPlugin("InstalledVersionPlugin"));
	_pAvailableVersionPlugin = dynamic_cast<AvailableVersionPlugin*>(requestPlugin("AvailableVersionPlugin"));
    pObserver->setProgressRange(97, 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);
}



}	// namespace NPlugin
