//
// C++ Interface: packagedisplaywidget
//
// Description: 
//
//
// Author: Benjamin Mesing <bensmail@gmx.net>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef __NPACKAGESEARCH_PACKAGEDISPLAYWIDGET_H_2007_10_30
#define __NPACKAGESEARCH_PACKAGEDISPLAYWIDGET_H_2007_10_30

#include <QTableWidget>
#include <QList>
class QAction;

#include <set>
#include <vector>
#include <list>
#include <map>
#include <algorithm>


// NXml
#include <ixmlstorable.h>

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

namespace NPlugin
{
	class ShortInformationPlugin;
	class ActionPlugin;
}


using namespace std;


namespace NPackageSearch {


inline auto shortInformationPluginCompare = [](NPlugin::ShortInformationPlugin const* const p1, NPlugin::ShortInformationPlugin const* const p2) {
    return (*p1) < (*p2);
};


/**
 * @brief This class is a widget which displays package information in a Table.
 *
 * The package information displayed is taken from all ShortInformationPlugins and
 * the action which can be performed are taken from the ActionPlugins. To get to know
 * about the plugins, this class implements the IPluginUser interface and needs to be
 * registered to all plugin providers.
 *
	@author Benjamin Mesing <bensmail@gmx.net>
*/
class PackageDisplayWidget : public QTableWidget, public NPlugin::IPluginUser, public NXml::IXmlStorable
{

    typedef NPlugin::ShortInformationPlugin ShortInformationPlugin;
	typedef NPlugin::ActionPlugin ActionPlugin;
	Q_OBJECT
	vector<ActionPlugin*> _actionPlugins;



    // the plugins where its column is shown
    set<ShortInformationPlugin*, decltype (shortInformationPluginCompare)> _shownPlugins;
    // the plugins where its column is hidden
    set<ShortInformationPlugin*> _hiddenPlugins;

    /** Shown and hidden plugins in no particular order.
     * Expensive operation.
     */
    set<ShortInformationPlugin*> allPlugins() const;

	QAction* _pCustomizeColumnsAction;
	/** The state for one column. */
	struct Column
	{
		Column()	{}
        Column(QString id, int width, int position, bool hidden ) :
            _id(id),
			_width(width),
			_position(position),
			_hidden(hidden)
		{
		}
        QString _id;
		int _width;
		int _position;
		bool _hidden;
	};
	/** This contains the column settings that were loaded during loadSettings().
	  *
	  * It maps the column title to the stored settings.
	  * The appropriate data will be applied a column when it is added.
	  */
	map<QString, Column> _loadedColumnSettings;
    int WIDGET_CHAR_WIDTH;
    int MARGIN;
    static inline int const COLUMN_SETTINGS_XML_VERSION = 3;
public:
	PackageDisplayWidget(QWidget * pParent = 0);
	~PackageDisplayWidget();


	/** @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);
	//@}
public slots:
	/** @brief Sets the packages to be displayed. */
	void setPackages(const set<string>& packages);
	/** Shows a dialog to control the settings of the columns (which columns are shown). */
	void showColumnControlDialog();
protected:
	/** @brief Updates the columns displayed.
	  *
	  * This includes updating of the number of columns, the column caption and the ordering.
	  */
	void updateColumnDisplay();
protected slots:
	virtual void onItemSelectionChanged();
	void onHeaderContextMenuRequested(const QPoint& pos);
signals:
	/** Emitted whenever a package was selected.
	  *
	  * Emitted with an empty string, if no package was selected.
	  */
	void packageSelected(QString package);


//////////////////////////////////////////////////
// Column display control
//////////////////////////////////////////////////
public:
	virtual void saveSettings(NXml::XmlData& outData, QDomElement parent) const;
	virtual QDomElement loadSettings(QDomElement source);
	/** @brief To be called once all columns are added, so that the loaded settings can be applied. 
	  *
	  * Applies all non-column specfic settings.
	  */ 
	virtual void initialize();
	
//////////////////////////////////////////////////
// Query Methods
//////////////////////////////////////////////////

	/** @name Query Methods
	  *
	  * These methods query some information without changing the class.
	  */
	//@{	begin Query Methods
	/** @brief Returns the package that is currently selected. 
	  *
	  * @return the name of the package or a null string, if no package was selected.
	  */
	QString selectedPackage() const;
	/** @brief Returns if the score column is visible. 
	  *
	  * This is useful to determine if score calculation should be performed.
	  */
	bool scoreColumnVisible() const;
	/** @brief Returns the package referred to by the given row. 
	  */
	QString packageForRow(int row) const;
	
	/** Debug Method: Prints the name of the columns in order of their visible index using qDebug(). */
	void debugPrintOrder() const;
private:
    /** Returns the logical index of the column where the header has the given name.
      *
      * @returns the index of the column or -1 if no such column was found.
      */
    virtual int columnForName(const QString& name) const;

    /** Returns the index of the column for this plugin.
      *
      * @returns the index of the column or -1 if no such column was found.
      */
    virtual int columnForPlugin(ShortInformationPlugin* pPlugin) const;

    virtual int columnForCaption(const QString& caption) const;

    template<typename UnaryPredicate>
    int findColumn(UnaryPredicate predicate) const {
        int index = 0;
        for (auto it = _shownPlugins.cbegin(); it != _shownPlugins.cend(); ++it, ++index) {
            if (predicate(*it))
                return index;
        }
        return -1;
    }

	/** Returns the plugin with the given caption.
	  *
	  * @returns the plugin or 0 if no such plugin exists.
	  */
    virtual ShortInformationPlugin* pluginForCaption(const QString& name, const set<ShortInformationPlugin*>& pluginsToSearch) const;

	/** @brief Returns the list of all actions provided by all action plugins. */
	virtual QList<QAction*> packageActions() const;
  	//@}	end Query Methods

	/** @brief Diplays a context menu showing the possible actions. 
	  *
	  * Reimplements QWidget::contextMenuEvent()
	  */
	virtual void contextMenuEvent(QContextMenuEvent * pEvent);

};

}

#endif	// __NPACKAGESEARCH_PACKAGEDISPLAYWIDGET_H_2007_10_30
