#include <cassert>

#include <qcombobox.h>
#include <qlineedit.h>
#include <QMainWindow>
#include <qtimer.h>
#include <qspinbox.h>
#include <qstatusbar.h>
#include <qpushbutton.h>
#include <qdebug.h>

#undef slots
#include <xapian.h>

#include <tagcoll/utils/set.h>
#include <ept/debtags/debtags.h>

// NUtil
#include <helpers.h>

// NPlugin
#include <iprovider.h>

// NPlugin
#include "relatedplugin.h"
#include "relatedinput.h"
#include "relatedfeedbackwidget.h"

// NUtil
#include "debtagshelper.h"

namespace NPlugin
{

const QString RelatedPlugin::PLUGIN_NAME = "RelatedPlugin";


RelatedPlugin::RelatedPlugin(const DebtagsPluginContainer& container)
	: _container(container)
{
	_pMainWindow = 0;
	_pProvider = 0;
	_pRelatedInput = 0;
	_pRelatedFeedbackWidget = 0;
	_isInactive = true;
}

RelatedPlugin::~RelatedPlugin()
{
	delete _pRelatedInput;
	delete _pRelatedFeedbackWidget;
}


/////////////////////////////////////////////////////
// Plugin Interface
/////////////////////////////////////////////////////

QString RelatedPlugin::title() const
{ 
	return tr("Similar Plugin"); 
}

QString RelatedPlugin::briefDescription() const
{
	return tr("Plugin for searching packages similar to another package.");
}

QString RelatedPlugin::description() const
{
	return tr("Plugin for searching packages similar to another package.");
}

void RelatedPlugin::init(IProvider* pProvider)
{
	_pProvider = pProvider;
	_pMainWindow = pProvider->mainWindow();
	
	_pRelatedInput = new RelatedInput(_pMainWindow, "RelatedInput");
	const set<string>& packages = pProvider->packages();
	for (set<string>::const_iterator it = packages.begin(); it != packages.end(); ++it)
		_pRelatedInput->_pPackageInput->addItem(toQString(*it));
	_pRelatedInput->_pPackageInput->setMinimumWidth(100);
	_pRelatedInput->_pPackageInput->setEditText("");
	connect(_pRelatedInput->_pPackageInput, SIGNAL(activated(const QString&)), SLOT(evaluateSearch()));
	connect(_pRelatedInput->_pClearButton, SIGNAL(clicked()), SLOT(onClearSearch()));
	
	_pRelatedFeedbackWidget = new RelatedFeedbackWidget(_pMainWindow, "RelatedFeedbackWidget");
	_pRelatedFeedbackWidget->setClearButton(
		pProvider->createClearButton(_pRelatedFeedbackWidget, "AptClearButton"), 0);
	connect(_pRelatedFeedbackWidget->_pClearButton, SIGNAL(clicked()), SLOT(onClearSearch()));

	_pRelatedFeedbackWidget->setShown(false);
	connect(
		_pRelatedInput->_pResultNumber, SIGNAL(valueChanged(int)), 
		SLOT(evaluateSearch())
	);
	connect(
		_pRelatedInput->_pPackageInput, SIGNAL(textChanged(const QString&)), 
		SLOT(onInputTextChanged(const QString&))
	);
}

/////////////////////////////////////////////////////
// SearchPlugin Interface
/////////////////////////////////////////////////////

QString RelatedPlugin::inputWidgetTitle() const	
{
	return tr("Similar"); 
}

QWidget* RelatedPlugin::inputWidget() const
{ 
	return _pRelatedInput;
}

QWidget* RelatedPlugin::shortInputAndFeedbackWidget() const
{
	return _pRelatedFeedbackWidget;
}

void RelatedPlugin::clearSearch()
{
	_pRelatedInput->_pPackageInput->setEditText("");
}

const std::set<string>& RelatedPlugin::searchResult() const
{
	return _searchResult;
}

/////////////////////////////////////////////////////
// ScorePlugin Interface
/////////////////////////////////////////////////////
	
bool RelatedPlugin::offersScore() const 
{
	return !isInactive();

}

void RelatedPlugin::ScoreCalculator::calculateScore(const set<string>& packages)
{
	for (set<string>::const_iterator it = packages.begin(); it != packages.end(); ++it) 
	{
		map<string,float>::const_iterator jt = _scores.find(*it);
		// should not happen 
		if (jt == _scores.end())
			setScore(*it, 0);
		setScore(*it, jt->second);
	}
}


map<string, float> RelatedPlugin::getScore(const set<string>& packages) const
{
	_calculator.calculateScore(packages);
	return _calculator.getScore();
	/*
	// the map that will be returned
	map<string, float> result;
	assert(!isInactive());
 	string package = toString(_pRelatedInput->_pPackageInput->currentText());
	int maxdist = _pRelatedInput->_pMaximumDistanceInput->value();
	std::set<Tag> ts = _pProvider->debtags().getTagsOfItem(package);
	for (set<string>::const_iterator it = packages.begin(); it != packages.end(); ++it)
	{
		int dist = tagcoll::utils::set_distance(ts,  _pProvider->debtags().getTagsOfItem(*it));
		result[*it] = float(maxdist - dist + 1) / (maxdist+1);
	}
	return result;
	*/
}


/////////////////////////////////////////////////////
// helper functions
/////////////////////////////////////////////////////

void RelatedPlugin::onInputTextChanged(const QString& text)
{
	if (text == "")
		evaluateSearch();
}

void RelatedPlugin::evaluateSearch()
{
	_pProvider->reportBusy(this, tr("Searching for similar packages"));
	_searchResult.clear();
	_calculator._scores.clear();
	_pRelatedFeedbackWidget->_pSimilarSearchTextView->setText(
		_pRelatedInput->_pPackageInput->currentText()
	);
 	string package = toString(_pRelatedInput->_pPackageInput->currentText());
	_isInactive = package.empty();	// if no input was given the search is inactive
	if ( !_isInactive )
	{
		int num = _pRelatedInput->_pResultNumber->value();
		
		std::pair<bool, Xapian::Document> document = documentForPackage(package, _pProvider->xapian());
		if (!document.first) 
		{
			qWarning() << QString("Empty result set for search XP") << toQString(package);
			return;
		}
		Xapian::Document& packageDocument = document.second;
		// search all documents containing any of the search terms taken from the original
		// package
		Xapian::Query query(Xapian::Query::OP_OR, packageDocument.termlist_begin(), packageDocument.termlist_end());
		Xapian::Enquire enq(_pProvider->xapian());
		enq.set_query(query);
		// get the <num> best matches
		Xapian::MSet matches = enq.get_mset(0, num);
		for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i)
		{
			string package = i.get_document().get_data();
			_searchResult.insert(package);
			_calculator._scores[package] = i.get_percent() / 100.0f;
		}
	}
	// show the feedback widget only if a search was entered
	_pRelatedFeedbackWidget->setShown(!_isInactive);
	_pProvider->reportReady(this);
	emit searchChanged(this);
}


void RelatedPlugin::setWidgetsEnabled(bool enabled)
{
	if (_pRelatedInput)
		_pRelatedInput->setEnabled(enabled);
}
 
}	// namespace NPlugin

#undef emit
#include <ept/debtags/debtags.tcc>
