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

#include <set>

#include <QAbstractListModel>

#include <debtagsplugincontainer.h>
#include <ept/debtags/vocabulary.h>


#include <helpers.h>

namespace NTagModel
{

using namespace std;

struct FacetData;
struct TagData;

/** @brief Base class for TagData and ItemData.
  *
  * Use by Vocabulary model to return the data.
  */
struct ItemData
{
	virtual ~ItemData() {};
	/** @brief Returns if the item is a facet. */
	virtual bool isFacet() const = 0;
	/** @brief Returns the name of the item in user presentable form (i.e. "Text" instead of
	  * "works-with-format:text"). 
	  */
	virtual QString name() const = 0;
	/** @brief Returns the full user presentable text for the item (i.e. "Format: Text" instead of
	  *  "works-with-format::text"). 
	  */
	virtual QString fullDisplayText() const = 0;
	/** @brief Returns the full technical name (e.g. "works-with-format" for a facet and
	  * "works-with-format::text" for a tag).
	  */
	virtual QString fullname() const = 0;
	/** Returns the description of the item. */
	virtual QString description() const = 0;
	virtual const FacetData* toFacetData() const = 0;
	virtual const TagData* toTagData() const = 0;
	virtual FacetData* toFacetData() = 0;
	virtual TagData* toTagData() = 0;
};

class FacetData : public ItemData
{
	typedef ept::debtags::voc::FacetData EptFacetData;
	
	const EptFacetData* _pEptFacetData;
public:
	bool hidden;
	int row;
	FacetData(const EptFacetData* facet_, int row_) :
		_pEptFacetData(facet_),
		hidden(false),
		row(row_)
	{

	}
	
	const EptFacetData* eptFacetData() const { return _pEptFacetData; }
	string facet() const { return _pEptFacetData->name; }
	virtual bool isFacet() const { return true; }
	virtual QString name() const { return toQString(_pEptFacetData->shortDescription()); }
	virtual QString fullname() const { return toQString(_pEptFacetData->name); }
	virtual QString fullDisplayText() const { return name(); };
	virtual QString description() const { return toQString(_pEptFacetData->shortDescription()); }
	virtual const FacetData* toFacetData() const { return this; };
	virtual const TagData* toTagData() const { return 0; };
	virtual FacetData* toFacetData() { return this; };
	virtual TagData* toTagData() { return 0; };
};

struct TagData : public ItemData
{
	typedef ept::debtags::voc::TagData EptTagData;
	
	const EptTagData* _eptTagData;
public:
	int facetIndex;
	/** @brief Stores if the tag was selected to be searched for.
	  *
	  * This has nothing to do with user selection like it is happening
	  * through the views.
	  */
	bool selected;
	TagData(const EptTagData* tag_, int facetIndex_) :
		_eptTagData(tag_),
		facetIndex(facetIndex_),
		selected(false)
	{
	}
	virtual bool isFacet() const { return false; }
	const string& tag() const { return _eptTagData->name; }
	virtual QString name() const { return toQString(_eptTagData->shortDescription()); }
	virtual QString fullname() const { return toQString(_eptTagData->name); }
	virtual QString fullDisplayText() const 
	{ QString tmp = toQString(ept::debtags::voc::getfacet(_eptTagData->name)); // FIXME: needs to access vocabulary for this: toQString(tag.facet().shortDescription());
		tmp += QString(": ");
		tmp += name();
		return tmp; };
	virtual QString description() const { return toQString(_eptTagData->longDescription()); }
	virtual const FacetData* toFacetData() const { return 0; };
	virtual const TagData* toTagData() const { return this; };
	virtual FacetData* toFacetData() { return 0; };
	virtual TagData* toTagData() { return this; };
};


/**
  *	@author Benjamin Mesing <bensmail@gmx.net>
  */
class VocabularyModel : public QAbstractItemModel
{
 Q_OBJECT
	typedef string Tag;
	typedef string Facet;

	/** @brief The object used to access the tag collection.
	  *
	  * If this is 0, the tag selection will not be accessible, and no companion tags
	  * will be determined (i.e. if you select a tag, the selection will not be limited 
	  * according to this tag.)
	  */
	const NPlugin::DebtagsPluginContainer* _pContainer;

	vector<FacetData*> _facets; 
	/** Maps the facet to the list of tags belonging to it. */
 	map<Facet,  vector<TagData*> > _tags; 
 	/** Maps the tag to the pair (facetIndex, tagIndex) identifying the tag. */
	map<Tag, pair<int, int> > _tagToTagData;
 	/** Maps the facet names to the index identifying the facet. */
	map<Facet, int > _facetToFacetData;

public:
	VocabularyModel() {};

	VocabularyModel(const NPlugin::DebtagsPluginContainer* pContainer);

	~VocabularyModel();

	/** @brief Sets the hidden state of the facet with the given name. */
	void setFacetHidden(bool hidden, string facet);

	/** @name AbstractItemModel interface
	  * 
	  * Implementation of the AbstractItemModel interface
	  */
	//@{
	virtual int rowCount(const QModelIndex & parent = QModelIndex() ) const;
	virtual int columnCount(const QModelIndex & = QModelIndex() ) const { return 2; };
	virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
	virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
	virtual QModelIndex parent(const QModelIndex & index ) const;
	virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex() ) const;
	//@}
	/** @brief Returns an index for the handed tag. 
	  *
	  * @param tag the tag to get the index for
	  * @param column the column for the row with the given tag
	  * @returns an invalid index if no such tag/facet exists
	  */
	virtual QModelIndex indexForTag(const Tag& tag, int column) const;

	
	/** @brief Allows to set the data for the VocabularyModeRole::Selected and the 
	  * VocabularyModeRole::Hidden role.
	  *
	  * Setting data for any other role is passed up to QTreeView::setData().
	  *
	  * @returns true if the data was set successfully, else false
	  * @pre index.isValid()
	  */
	virtual bool setData(const QModelIndex& index, const QVariant& value, int role);
	
	/** @brief Sets VocabularyModeRole::Selected to false for all tags.
	  */
	virtual void setAllUnselected();

	/** Gets the index'th facet in the current model. 
	  *
	  * Precondition: 0 &lt;= i &lt; facetNum
	  */
	Facet getFacet(int index) const;

	/** @brief Returns the facets that are hidden. */
	set<Facet> hiddenFacets() const;
	/** @brief Returns the facets that are hidden. */
	set<Facet> shownFacets() const;

	/** Returns the tags selected for this model. */
	const set<Tag>& selectedTags() const;

	/** Gets the index'th object in the given set. 
	  *
	  * Precondition: 0 &lt;= i &lt; collection.size()
	  */
	template<class T> const T& getElement(std::set<T> collection, int index) const
	{
		typedef set<T> set_type;
		int i = 0;
		typename set_type::const_iterator it = collection.begin();
		while (i != index)
		{
			++i;
			++it;
		}
		return *it;
	}
signals:
	void selectionChanged();
protected:
	/** @brief Helper method that sets all items beneath index and index itself as unselected. 
	  *
	  * It will not emit any dataChanged signals.
	  */
	void setAllUnselected(QModelIndex index);


	/** Collects the tags from the model that are selected. 
	  * 
	  * @pre parent != QModelIndex() ( which means that the children of parent 
	  * must point be tags (i.e. no facets)
	  */
	std::set<Tag> collectSelectedChildItems(const QModelIndex& parent) const;
//private:
public:
	/** Keeps track of the tags which are selected. */
	std::set<Tag> _selectedTags;
	// TODO: access for companion tags to be implemented (including caching)
	//mutable bool _companionTagsValid;
	// TODO: access for companion tags to be implemented (including caching)
	//mutable std::set<Tag> _companionTagsChache;
	void emitAllDataChanged();
};

}

struct TagWrapper
{
	typedef std::string Tag;
public:
	Tag tag;
	TagWrapper();
	TagWrapper(Tag tag_);
};

Q_DECLARE_METATYPE(TagWrapper)


#endif	// __NTAGMODEL_VOCABULARYMODEL_H_2006_10_01
