//
// C++ Interface: packagesearchimpl
//
// Description: 
//
//
// Author: Benjamin Mesing <bensmail@gmx.net>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//

#ifndef __PACKAGESEARCHIMPL_H_2004_03_21
#define __PACKAGESEARCHIMPL_H_2004_03_21

#include <utility>
#include <vector>
#include <map>

#include <QTimer>

#include "ui_packagesearch.h"

// NPlugin
#include "iprovider.h"
#include "ipluginuser.h"

// NBrowser
#include "history.h"
#include "networksettings.h"


class QCloseEvent;
class QDir;
class QToolBar;
class QSplitterHandle;

class TagSelection;

namespace NPlugin
{
	class Plugin;
	class ActionPlugin;
	class SearchPlugin;
	class InformationPlugin;
	class ShortInformationPlugin;
	class PluginManager;
	class ScoreDisplayPlugin;
}

namespace NPackageSearch
{
	class PackageDisplayWidget;
}

namespace Xapian {
	class Database;
}

namespace ept 
{
	namespace apt {
		class Apt;
	}
	namespace debtags {
		class Vocabulary;
	}
}


using namespace std;


/** Predicate to compare pointer to plugins by priority. */
template <typename PluginType> 
class LesserPriority : binary_function<PluginType*, PluginType* ,bool>
{
public:
	bool operator()(PluginType* p1, PluginType* p2);
};

/** @brief The central class for the application, creating all the relevant objects.
  * 
  * @author Benjamin Mesing
  */
class PackageSearchImpl : public QMainWindow, public Ui::PackageSearch, public NPlugin::IProvider, public NPlugin::IPluginUser
{
    Q_OBJECT
public:
	typedef set<NPlugin::Plugin*> PluginContainer;
	typedef set<NPlugin::SearchPlugin*, LesserPriority<NPlugin::SearchPlugin> > SearchPluginContainer;
	typedef set<NPlugin::InformationPlugin*, LesserPriority<NPlugin::InformationPlugin> > InformationPluginContainer;
	typedef set<NPlugin::ShortInformationPlugin*, LesserPriority<NPlugin::ShortInformationPlugin> > ShortInformationPluginContainer;

    PackageSearchImpl( QWidget* parent = 0, const char* name = 0, Qt::WindowFlags fl = Qt::Window );
	~PackageSearchImpl();
	struct Exception
	{
		Exception(const string& errorMsg) : _errorMsg(errorMsg) {}
		Exception(const char* errorMsg) : _errorMsg(errorMsg) {}
		string _errorMsg;
	};
	/** @name IProvider interface
	  *
	  * These functions implement the IProvider interface.
	  */
	//@{	
	virtual QString iconDir() const	{ return _iconDir; };
	virtual void setEnabled(bool enabled);
	virtual QString currentPackage() const	{ return _currentPackage; };
	virtual QPushButton* createClearButton(QWidget* pParent = 0, const char* name = 0) const;
	virtual void reportError(const QString& title, const QString& message);
	virtual void reportWarning(const QString& title, const QString& message);
	virtual void reportBusy(NPlugin::Plugin* pPlugin, const QString& message);
	virtual void reportReady(NPlugin::Plugin* pPlugin);
	virtual QMainWindow* mainWindow() { return this; }
	virtual QStatusBar* statusBar() { return QMainWindow::statusBar(); }
	virtual const set<string>& packages() const;
	virtual NUtil::IProgressObserver* progressObserver() const;
	virtual void reloadAptFrontCache();
	const ept::apt::Apt& apt() const { return *_pApt; }
	const ept::debtags::Vocabulary& vocabulary() const { return *_pVocabulary; }
	const Xapian::Database& xapian() const { return *_pXapianDatabase; }
	virtual QNetworkAccessManager* network();
	//@}
	/** @name IPluginUser interface
	  *
	  * These functions implement the IPluginUser interface.
	  */
	//@{	
	// documented in base class
	virtual void addPlugin(NPlugin::Plugin* pPlugin);
	/** @brief Finds every reference to plugin and remove it so that pPlugin can be safely deleted.
	  *
	  * @param pPlugin the plugin to be removed 
	  */
	void removePlugin(NPlugin::Plugin* pPlugin);
	//@}
	/** @brief Adds the given plugin to the SearchWindow.
	  *
	  * @param pPlugin the plugin to be added
	  */
	virtual void addPlugin(NPlugin::PluginContainer* pPlugin);
	
	/** @brief Sets the directory were translations are stored. */
	virtual void setTranslationDir(const QDir& dir);
public slots:
	/** Call this function after the dialog is shown to initialize the whole system. */
	void initialize();
	
	/** @brief This method checks, if the apt-cache has changed and if neccessary reloads
	  * the apt database. 
	  *
	  * Currently it is periodically called by a timer.
	  */
	void checkAptDbForUpdates();
protected slots:
	/** @brief This let the views show all information available about the package.
	  *
	  * This request all plugins to update their information widgets and gets their
	  * information text.\n
	  * If package is empty, the views will be cleared. 
	  * @note Do not call this function directly, but call setCurrentPackage() instead.
	  */
	virtual void updatePackageInformation(const QString & package);
	/** @brief Evaluates the plugin search results and displays them in the GUI.
	  *
	  * This is called whenever the search of a SearchPlugin changes.
	  * @param pPlugin the plugin where the search changed or 0 if no originator is known
	  */
	virtual void onSearchChanged(NPlugin::SearchPlugin* pPlugin);
	virtual void onClearSearch();
	/** This is called whenever the user selects another information page. */
	virtual void onInformationPageChanged();
	/** This shows the control dialog for the plugins. */
	virtual void onControlPlugins();
	
	/** This shows a settings dialog. */
	virtual void onPreferences();
	virtual void saveSettings();
	/** @brief Loads the settings for packagesearch and the plugins.
	  *
	  * Must be called after setting _pPluginManager but before calling loadPlugins().
	  * The settings will be stored in a data structure, and applied when the plugins
	  * are loaded.
	  */
	virtual void loadSettings();
	
	void onLinkClicked(const QUrl& link);
	void onForward();
	void onBack();
	
	/** @brief Called, whenever the selected package in the package list changes.
	  *
	  * It calls selectNewPackage if package is not empty, otherwise it does nothing
	  * (and thus _currentPackage remains unchanged).
	  */
	void onPackageSelectionChanged(QString package);
	
	/** @brief This chooses the given package to be shown and adds it to the history if
	* different from the current entry.
	*
	* If it is called with an empty or a null string, it deselect the currently selected
	* package and set the current package to be empty.
		* @see setCurrentPackage()
	*/
	void selectNewPackage(const QString & package);
	/** @brief This sets the package selected for display to package.
		*
		* It updates the combobox which shows the package which triggers the showPackageInformation()
		* function. It does not add an entry to the history.
		*
		* @param package the new current package, should be a valid package
		* @see selectNewPackage()
		*/
	void setCurrentPackage( const QString & package );
	/** @brief Enables/ Disables the browse buttons */
	void updateBrowseToolbar();
	void onHelpContentsAction();
	void onHelpAboutAction();
	/** @brief Sets the number of packages that were found.
	*
	* This information is displayed in the dialog. 
	* @param number number of packages found, hand -1 if no search is active */
	void setPackagesFound( int number );
	
protected:
	/** @brief Returns the package view (which is a QTableWidget) casted to PackageDisplayWidget. */
	NPackageSearch::PackageDisplayWidget* packageView();
	/** @brief Called by addPlugin(NPlugin::Plugin*) if the plugin is an short information plugin.
	  *
	  * This was added to improve readability of the addPlugin() function.
	  */
	void addShortInformationPlugin(NPlugin::ShortInformationPlugin* pPlugin);
	/** @brief Called by addPlugin(NPlugin::Plugin*) if the plugin is an short information plugin.
	  *
	  * This was added to improve readability of the addPlugin() function.
	  */
	void addActionPlugin(NPlugin::ActionPlugin* pPlugin);
	/** Actions to perform on close. */
	virtual void closeEvent(QCloseEvent* pE);


	/** @brief Funtion object to filter search results with plugins which uses the filter 
	  * technique.  */
	class FilterPackages
	{
		NPlugin::SearchPlugin* _pPlugin;
	public:
		/** @param pPlugin the plugin to be used for filtering. 
		  * @pre pPlugin.isInactive() == false ^ pPlugin.usesFilterTechnique() == true  */
		FilterPackages(NPlugin::SearchPlugin* pPlugin) : _pPlugin(pPlugin)	{};
		bool operator()(const string& package);
		/** Call this after you've finished all filtering - the results will be written to the 
		  * OpSet handed in the constructor. */
// 		void finishFiltering();
	};
	
	/** @brief Updates search interface.
	  *
	  * Shows the search input for all available search plugins in the correct order,
	  * removing outdated inputs. */
	void updateSearchPluginGui();
	/** @brief Adds the widgets of the search plugin to the GUI. 
	  * 
	  * @pre pPlugin != 0  */
	void addSearchPluginToGui(NPlugin::SearchPlugin* pPlugin);
	/** @brief Updates information interface.
	  *
	  * Shows the information widget for all available information plugins in the correct 
	  * order, removing outdated widgets. */
	void updateInformationPluginGui();
	/** @brief Sets all the actions operating on packages to be enabled/disabled. 
	  *
	  * @see #_packageActions
	  */
	void setPackageActionsEnabled(bool enabled);
	/** @brief Returns the ShortInformationPlugin with <i>caption</i>.
	  *
	  * @returns the plugin found or 0 if no plugin was found*/
	NPlugin::ShortInformationPlugin* getShortInformationPluginWithCaption(const QString caption) const;
	/** @brief Runs update-apt-xapian-index as root and returns only after it is finished. */
	void updateXapianDatabase();
private:
	/** @brief Sets up the missing toobar icons. */
	void initToolbars();
	/** @brief Sets up the missing menu entries. 
	  *
	  * @pre initToolbars() must have been called, because it relies on it to be initialized.
	  */
	void initMenus();
	/** @brief Determines the directories for icons, help files, etc. */
	void initDirectories();

    void makeHandleVisible(QSplitterHandle* pSplitterHandle);
	
	ept::apt::Apt* _pApt;
	ept::debtags::Vocabulary* _pVocabulary;
	Xapian::Database* _pXapianDatabase;
	QString _iconDir;
	QString _docDir;
	/** Holds the name of the package currently shown in the information container. */
	QString _currentPackage;
	NBrowser::History<QString> _viewHistory;
	/** @brief Maps the toolbar names to the corresponding toolbar. */
	map<QString, QToolBar*> _nameToToolBar;
	/** @brief Maps the menu names to the corresponding menu. */
	map<QString, QMenu*> _nameToMenu;
	QToolBar* _pMainToolBar;
	QAction* _pWhatsThisAction;
	/** @brief Holds all the actions that operate on packages.
	  *
	  * Those will be enabled and disabled, depending if a package was selected.
	  */
	list<QAction*> _packageActions;
	/** @brief This object takes care of the plugins loading and unloading. 
	  *
	  * <tt>this</tt> will be added to it as a IPluginUser, so it will be informed about
	  * changes in the plugin. */
	NPlugin::PluginManager* _pPluginManager;
	
	/** All search plugins currently installed.
	  *
	  * They are kept sorted by there priority in ascending order.
	  */
	SearchPluginContainer _searchPlugins;
	/** @brief All information plugins currently installed.
	  *
	  * They are kept sorted by there priority in ascending order.
	  */
	InformationPluginContainer _informationPlugins;
	/** @brief All short information plugins currently installed.
	  *
	  * They are kept sorted by there priority in decending order.
	  */
	ShortInformationPluginContainer _shortInformationPlugins;
	
	/** @brief This holds a pointer to the page for the details section. */
	QWidget* _pInformationDetailsPage;
	/** Holds the priority for the details page. */
	const uint _DETAILS_INFORMATION_PRIORITY;
	/** Holds the name of the settings file to be used ($HOME/.packagesearch)*/
	QString _settingsFilename;
	/** @brief This holds all packages available on the system.
	  *
	  * It bases on the apt-cache so it assumes those packages as all. It is
	  * lazy initialized.
	  */
	mutable set<string> _packages;
	/** @brief This plugin is used to display the package names in the short description. */
	NPlugin::Plugin* _pPackageNamePlugin;
	/** @brief This plugin is used to display and calculate the scores for the packages. */
	NPlugin::ScoreDisplayPlugin* _pScoreDisplayPlugin;
	/** @brief Timer used to periodically call checkAptDbForUpdates(). */
	QTimer timer;
	NUtil::NetworkSettings _network;
};

#endif // __PACKAGESEARCHIMPL_H_2004_03_21
