//
// C++ Implementation: vocabularymodel
//
// Description: 
//
//
// Author: Benjamin Mesing <bensmail@gmx.net>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "vocabularymodel.h"


// NTagModel
#include "vocabularymodelrole.h"

#include <cassert>

#include <helpers.h>

typedef std::string Tag;
typedef std::string Facet;


TagWrapper::TagWrapper() {}
TagWrapper::TagWrapper(Tag tag_) : tag(tag_) {}

namespace NTagModel
{

VocabularyModel::VocabularyModel(const NPlugin::DebtagsPluginContainer* pContainer)
{
	_pContainer = pContainer;
	const set<Facet> facets = _pContainer->facets(); 
	int i = 0;
	const ept::debtags::Vocabulary* dtvoc = &_pContainer->vocabulary();
	for (set<Facet>::const_iterator it = facets.begin(); it != facets.end(); ++it, ++i)
	{
		Facet facet = *it;
		const ept::debtags::voc::FacetData* fd = dtvoc->facetData(facet);
		if (!fd) continue;
		_facets.push_back(new FacetData(fd, i));
		_facetToFacetData[facet] = _facets.size()-1;
		set<Tag> tags = fd->tags();
		vector<TagData*> tagData;
		int j = 0;
		for (set<Tag>::const_iterator jt = tags.begin(); jt != tags.end(); ++jt, ++j)
		{
			const ept::debtags::voc::TagData* td = dtvoc->tagData(*jt);
			if (!td) continue;
			tagData.push_back( new TagData(td, i) );	
			_tagToTagData[td->name] = make_pair(i, j);
		}
		_tags[facet] = tagData;
	}
	//_companionTagsValid = false;

}


VocabularyModel::~VocabularyModel()
{
}

void VocabularyModel::setFacetHidden(bool hidden, string facet)
{
	// if the facet exists
	if (_facetToFacetData.find(facet) != _facetToFacetData.end())
	{
		int intIndex = _facetToFacetData.find(facet)->second;
		QModelIndex modelIndex = this->index(intIndex, 0, QModelIndex());
		setData(modelIndex, hidden, HiddenRole);
	}
}


int VocabularyModel::rowCount(const QModelIndex & parent) const
{
	// if the number of facets was requested
	if (!parent.isValid())
	{
		return _facets.size();
	}
	else 
	{
		// only the first column has any children
		if (parent.column() != 0)
			return 0;
		const ItemData* pData = (ItemData*) parent.internalPointer();
		assert(pData != 0);
		// if the parent is a facet, the number of children is the number of tags in the facet
		if (pData->isFacet())
		{
			const FacetData* pParentData = (FacetData*) pData;
			return pParentData->eptFacetData()->tags().size();
		}
		else	// parent is a tag - tags do not have children (since tag grouping is not supported) supported)
			return 0;
	}
}

QVariant VocabularyModel::data(const QModelIndex& index, int role) const
{
// 	qDebug("Requesting data for row: %d, col: %d", index.row(), index.column());
	// sometime an item -1, -1 is requested
	if (index.row() < 0 || index.column() < 0)
		return QVariant();
	switch (role)
	{
		case Qt::ToolTipRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			assert(pData != 0);
			return pData->description();
		}
		case Qt::DisplayRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			assert(pData != 0);
			if (index.column() == 1)
				return pData->fullname();
			return pData->name();
		}
		case FullDisplayRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			assert(pData != 0);
			if (index.column() == 1) {
				// VocabularyModel::data() should not be called with FullDiplayRole and index.column(), 
				// it should be used only by the list view
				assert(false);
				return "";
			}
			return pData->fullDisplayText();
		}
		case SelectedRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			if (!pData->isFacet())
			{
				const TagData* pTagData = pData->toTagData();
				return pTagData->selected;
			}
			else
				return false;
		}
		case HiddenRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			if (pData->isFacet())
				return pData->toFacetData()->hidden;
			else
				return false;
		}
		case TypeRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			if (!pData->isFacet())
			{
				return TagTypeItem;
			}
			else
				return FacetTypeItem;
		}
		case TagRole:
		{
			const ItemData* pData = (ItemData*) index.internalPointer();
			assert(pData->toTagData());
			Tag tag = pData->toTagData()->tag();
			// dummy to pull in default constructor symbol
// 			TagWrapper tw();
 			return QVariant::fromValue(TagWrapper(tag));
// 			return QVariant();
		}
	}
	return QVariant();
}

QVariant VocabularyModel::headerData( int section, Qt::Orientation, 
	int role) const
{
	if (role == Qt::DisplayRole)
	{
		if (section == 0)
			return "Name";
		if (section == 1)
			return "ID";
	}
	return QVariant();
}

QModelIndex VocabularyModel::parent(const QModelIndex & index) const
{
	if (!index.isValid())
		return QModelIndex();
	const ItemData* pData = (ItemData*) index.internalPointer();
	assert(pData != 0);
	if (pData->isFacet())
	{
// 		qDebug("[parent()] Parent is facet: " + pData->fullname());
		return QModelIndex();	// no parent for facets
	}
	else
	{
		const TagData* pTagData = static_cast<const TagData*>(pData);
		FacetData* facetData = _facets.at(pTagData->facetIndex);
// 		qDebug("[parent()] Getting parent for tag " + pTagData->fullname() + " with facet " +
// 			facetData.fullname() + " at row %d", facetData.row);
		return createIndex(facetData->row, 0, 
				const_cast<void*>(dynamic_cast<const void*>(_facets[pTagData->facetIndex])) );
	}
}

QModelIndex VocabularyModel::index (int row, int column, const QModelIndex & parent) const
{
// 	qDebug("[VocabularyModel::index()] requesting index row %i, col %i", row, column);
	
	// requesting index for a facet
	if (row < 0 || column < 0)
		return QModelIndex();	
	if (!parent.isValid())
	{
		if (row >= (int) _facets.size() || column >= 2)
		{
 			qWarning("[VocabularyModel::index()] Warning: row or column to large, row: %d, column, %d", row, column);
			return QModelIndex();	
		}
		const FacetData* pFd = _facets[row];
 		return createIndex(row, column, const_cast<void*>(dynamic_cast<const void*>(pFd)));
	}
	const ItemData* pData = (ItemData*) parent.internalPointer();
	if (!pData->isFacet())
		return QModelIndex();
	assert(dynamic_cast<const FacetData*>(pData) != 0);
	FacetData* pFacetData = (FacetData*) pData;
	const string facet = pFacetData->facet();
	const vector<TagData*>& tagDataOfFacet = _tags.find(facet)->second;
	if (row >= (int) tagDataOfFacet.size() || column >= 2)
	{
		qDebug("[VocabularyModel::index()] Warning: row or column to large, row: %d, column, %d", row, column);
		qStrDebug("[VocabularyModel::index()] Facet: " + pFacetData->fullname());
		return QModelIndex();
	}
	const TagData* pTd = tagDataOfFacet[row];
 	return createIndex( row, column, (void*)  pTd);
}


bool VocabularyModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
	switch(role)
	{
		case SelectedRole:
		{
			ItemData* pData = (ItemData*) index.internalPointer();
			TagData* pTagData = pData->toTagData();
			// setting selected for facets is not allowed/ignored
			if (!pTagData)
				return false;
			pTagData->selected = value.toBool();
			qDebug("[VocabularyModel::setData()] size before insert/remove: %lu", _selectedTags.size());
			// if the tag is selected
			if (value.toBool())
				_selectedTags.insert(pTagData->tag());
			else	// the tag is unselected
				_selectedTags.erase(pTagData->tag());
			qDebug("[VocabularyModel::setData()] size after insert/remove: %lu", _selectedTags.size());
			//_companionTagsValid = false;
// 			reset();
// 			emitAllDataChanged();
			emit(selectionChanged());
 			emit(dataChanged(index, index));
			return true;
		}
		case HiddenRole:
		{
			ItemData* pData = (ItemData*) index.internalPointer();
			if (pData->isFacet())
			{
				pData->toFacetData()->hidden = value.toBool();
				emit(dataChanged(index, index));
				return true;
			}
			else
			{
				qWarning("[VocabularyModel::setData()] trying to set hidden for a tag which is not supported");
			}
		}
		default:
			return QAbstractItemModel::setData(index, value, role);
	}
	return false;
}

void VocabularyModel::setAllUnselected()
{
	beginResetModel();
	QModelIndex parent = QModelIndex();
	for(int i = 0; i < rowCount(parent); ++i)
	{
		QModelIndex newParent = index(i, 0, parent);
		setAllUnselected(newParent);
	}
	//_companionTagsValid = false;
	endResetModel();}

void VocabularyModel::setAllUnselected(QModelIndex index)
{
	for(int i = 0; i < rowCount(index); ++i)
	{
		QModelIndex newParent = this->index(i, 0, index);
		setAllUnselected(newParent);
	}
	ItemData* pData = (ItemData*) index.internalPointer();
	TagData* pTagData = pData->toTagData();
	// if this is a tag
	if (pTagData)
	{
		pTagData->selected = false;
		_selectedTags.erase(pTagData->tag());
	}
	//_companionTagsValid = false;
}



void VocabularyModel::emitAllDataChanged()
{
	QModelIndex root = QModelIndex();
	for(int i = 0; i < rowCount(root); ++i)
	{
		QModelIndex parent = index(i, 0, root);
		QModelIndex tl = index(0, 0, parent);
		QModelIndex br = index(rowCount(parent)-1, columnCount(parent)-1, parent);
		qStrDebug("Top Left Item: " + data(tl, Qt::DisplayRole).toString());
		qStrDebug("Bottom Right Item: " + data(br, Qt::DisplayRole).toString());
		emit(dataChanged(tl, br));
	}
	QModelIndex parent = index(0, 0, root);
	QModelIndex br = index(rowCount(root)-1, columnCount(parent)-1, root);
	emit(dataChanged(parent, br));
}


QModelIndex VocabularyModel::indexForTag(const Tag& tag, int column) const
{
	map<Tag, pair<int, int> >::const_iterator it = _tagToTagData.find(tag);
	if (it == _tagToTagData.end())
		return QModelIndex();
	pair<int, int> indexes = it->second;
	int facetIndex = indexes.first;
	const string facet = _facets[facetIndex]->facet();
	int tagIndex = indexes.second;
	const TagData* pTd = _tags.find(facet)->second[tagIndex];
	return createIndex( tagIndex, column, (void*)  pTd);
}


Facet VocabularyModel::getFacet(int index) const
{
	return getElement(_pContainer->facets(), index);
}

const set<Tag>& VocabularyModel::selectedTags() const
{
	return _selectedTags;
}
	
set<Facet> VocabularyModel::hiddenFacets() const
{
	set<Facet> result;
	// iterate over all facets and collect the hidden ones
	QModelIndex parent = QModelIndex();
	for(int i = 0; i < rowCount(parent); ++i)
	{
		QModelIndex index = this->index(i, 0, parent);
		NTagModel::FacetData* pFacet = (NTagModel::FacetData*) index.internalPointer();
		if (pFacet->hidden)
			result.insert(pFacet->facet());
	}
	return result;
}

set<Facet> VocabularyModel::shownFacets() const
{
	set<Facet> result;
	// iterate over all facets and collect the shown ones
	QModelIndex parent = QModelIndex();
	for(int i = 0; i < rowCount(parent); ++i)
	{
		QModelIndex index = this->index(i, 0, parent);
		NTagModel::FacetData* pFacet = (NTagModel::FacetData*) index.internalPointer();
		if (!pFacet->hidden)
			result.insert(pFacet->facet());
	}
	return result;
}

std::set<Tag> VocabularyModel::collectSelectedChildItems(const QModelIndex& parent) const
{
	std::set<Tag> result;
	for(int i = 0; i < rowCount(parent); ++i)
	{
		QModelIndex index = this->index(i, 0, parent);
		// if the item is selected
		if (data(index, NTagModel::SelectedRole).toBool())
		{
			NTagModel::TagData* pTagData = (NTagModel::TagData*) index.internalPointer();
			result.insert(pTagData->tag());
        }
        auto collectSelectedChildItemsResult = collectSelectedChildItems(index);
        result.insert(collectSelectedChildItemsResult.begin(), collectSelectedChildItemsResult.end());
	}
	return result;
}


}	// NTagModel

#undef emit
//#include <ept/cache/tag.tcc>
//#include <ept/cache/debtags/vocabulary.tcc>
