#include <cassert>

#include <qmessagebox.h>
#include <qaction.h>
#include <QMainWindow>
#include <QMutex>

#include <unistd.h>		// for getuid and geteuid
#include <sys/types.h>	//

#include "debtagsplugincontainer.h"

#undef slots
#include <xapian.h>


// NUtil
#include "helpers.h"

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

// NPlugin
#include <iprovider.h>
#include <plugincontainer.h>
#include <iprogressobserver.h>
#include "debtagsplugin.h"
#include "relatedplugin.h"
#include "debtagspluginfactory.h"

// NTagModel
#include "vocabularymodel.h"


#include <globals.h>

#include "debtagssettingswidget.h"

extern "C" 
{ 
	NPlugin::PluginContainer* new_debtagsplugin() 
	{
		return new NPlugin::DebtagsPluginContainer;
	} 

	NPlugin::PluginInformation get_pluginInformation()
	{
		return NPlugin::PluginInformation("debtagsplugin", toString(NPackageSearch::VERSION), "Benjamin Mesing");
	} 
}

/** Initialize the plugin. */
__attribute__ ((constructor)) void init() 
{
}
      
      
// __attribute__ ((destructor)) void fini() 
// {
//   /* code here is executed just before dlclose() unloads the module */
// } 


namespace NPlugin
{

DebtagsPluginContainer::DebtagsPluginContainer()
{
	// this assumes, that only one instance of the DebtagsPluginContainer is created
	// (no two versions of libapt-front may be opened at any time)
	// This is ok for our purpose - though usually there should be a singleton ensuring
	// this constraint...
	DebtagsPluginFactory::getInstance()->setContainer(this);
	_pCommand = 0;
	_pRelatedPlugin = 0;
	_pDebtagsPlugin = 0;
	_pSettingsWidget = 0;
	_pVocabularyModel = 0;
	addPlugin("DebtagsPlugin");
	addPlugin("RelatedPlugin");
	
	_debtagsEnabled=false;
	
}
 
DebtagsPluginContainer::~DebtagsPluginContainer()
{
	unloadAllPlugins();
	delete _pCommand;
}

/////////////////////////////////////////////////////
// PluginContainer Interface
/////////////////////////////////////////////////////


bool DebtagsPluginContainer::init(IProvider* pProvider)
{
	BasePluginContainer::init(pProvider, DebtagsPluginFactory::getInstance());
	

	NUtil::IProgressObserver* pProgressObserver = provider()->progressObserver();
	if (pProgressObserver)
		pProgressObserver->setText("Loading Debtags Plugin");
	if (!_vocabulary.hasData())
	{
		setDebtagsEnabled(false);	// disable the debtags system
		provider()->reportError(
			tr("No vocabulary available" ),
			tr(
				"<p>The vocabulary is not available. This should not happen. Please reinstall "
				"<tt>debtags</tt> or check your /var/lib/debtags/vocabulary file manually.</p>"
				"The debtags plugin will be disabled for now, you can re-enable it via the Packagesearch menu -> Control Plugins."
				"</p>")
/*				"<p>"
				"The original error message reported by debtags was:<br>"
				"<tt>"
			) +
			toQString(e.desc()) +
			"</tt>"
			"</p>"*/
		);
		return false;
	}

	setDebtagsEnabled(true);

	_pVocabularyModel = new NTagModel::VocabularyModel(this);
	
	// use dynamic cast here because of the virtual base class 
	// (static_cast is not allowed there)
	_pRelatedPlugin = dynamic_cast<RelatedPlugin*>(requestPlugin("RelatedPlugin"));
	_pDebtagsPlugin = dynamic_cast<DebtagsPlugin*>(requestPlugin("DebtagsPlugin"));
	
	// inform the plugins about the new collections
/*	if (_pDebtagsPlugin)
		_pDebtagsPlugin->debtagsDataChanged();
	if (_pRelatedPlugin)
		_pRelatedPlugin->debtagsDataChanged();
	if (pProgressObserver)
		pProgressObserver->setProgress(100);*/
	
	return true;
}

vector< pair<QString, QAction*> > DebtagsPluginContainer::actions()
{
	vector< pair<QString, QAction*> > result;
	return result;
}

QWidget* DebtagsPluginContainer::getSettingsWidget(QWidget* pParent)
{
	_pSettingsWidget = new DebtagsSettingsWidget(_pVocabularyModel, pParent, "DebtagsSettingsWidget");
 	return _pSettingsWidget;
}

void DebtagsPluginContainer::applySettings()
{
	// nothing to be done here, settings are applied automatically
}



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


QDomElement DebtagsPluginContainer::loadContainerSettings(const QDomElement source)
{
	if (source.tagName() != "ContainerSettings")
		return source;
	float settingsVersion;
	NXml::getAttribute(source, settingsVersion, "settingsVersion", 0.0f);
	
	QDomNodeList hiddenFacets = source.elementsByTagName("HiddenFacet");
	for (int i=0; i < hiddenFacets.count(); ++i)
	{
		string hiddenFacet = toString( hiddenFacets.item(i).toElement().text() );
		_pVocabularyModel->setFacetHidden(true, hiddenFacet);
	}
	return NXml::getNextElement(source);
}

void DebtagsPluginContainer::saveContainerSettings(NXml::XmlData& outData, QDomElement parent) const
{
	qDebug("saveContainerSettings called");
	typedef std::string Facet;
	QDomElement containerElement = outData.addElement(parent, "ContainerSettings");
	outData.addAttribute(containerElement, 0.1f, "settingsVersion");
	set<Facet> hiddenFacets = _pVocabularyModel->hiddenFacets();
	for (set<Facet>::const_iterator it = hiddenFacets.begin(); it != hiddenFacets.end(); ++it)
	{
		QDomElement hiddenFacetElement = outData.addElement(containerElement, "HiddenFacet");
		outData.addText(hiddenFacetElement, *it);
	}
}





/////////////////////////////////////////////////////
// Helper Methods
/////////////////////////////////////////////////////

const Xapian::Database& DebtagsPluginContainer::xapian() const
{
	return provider()->xapian();
}

void DebtagsPluginContainer::setDebtagsEnabled(bool enabled)
{
	_debtagsEnabled = enabled;
}

const std::set<DebtagsPluginContainer::Facet> DebtagsPluginContainer::facets() const
{
	const Vocabulary& tags = provider()->vocabulary();
	return tags.facets(); 
}




}	// namespace NPlugin

#undef emit

