//
// Groups results.
//
void ResultsTree::regroupResults(bool groupBySearchEngine)
{
	ResultsModelColumns::ResultType currentType, newType;

#ifdef DEBUG
	cout << "ResultsTree::regroupResults: called" << endl;
#endif
	// What's the new grouping criteria ?
	if (groupBySearchEngine == true)
	{
		// By search engine
		currentType = ResultsModelColumns::RESULT_HOST;
		newType = ResultsModelColumns::RESULT_ROOT;
	}
	else
	{
		// By host
		currentType = ResultsModelColumns::RESULT_ROOT;
		newType = ResultsModelColumns::RESULT_HOST;
	}

	// Go through tree rows
	TreeModel::Children children = m_refStore->children();
	if (children.empty() == true)
	{
		return;
	}

	// Clear the map
	m_resultsGroups.clear();

	// Unselect results
	get_selection()->unselect_all();

	TreeModel::Children::iterator iter = children.begin();
	while (iter != children.end())
	{
		TreeModel::Row row = *iter;
#ifdef DEBUG
		cout << "ResultsTree::groupBySearchEngine: looking at " << row[m_resultsColumns.m_text] << endl;
#endif
		ResultsModelColumns::ResultType type = row[m_resultsColumns.m_type];
		// Skip new type rows
		if (type == newType)
		{
			iter++;
			continue;
		}

		TreeModel::Children child = iter->children();
		if (child.empty() == false)
		{
			TreeModel::Children::iterator childIter = child.begin();
			// Type RESULT_TITLE
			while (childIter != child.end())
			{
				TreeModel::Row childRow = *childIter;
				TreeModel::iterator groupIter, newIter;
				bool success = false;

				// We will need the URL and engines columns in all cases
				string url = from_utf8(childRow[m_resultsColumns.m_url]);
				unsigned int engineIds = childRow[m_resultsColumns.m_engines];
				unsigned int indexIds = childRow[m_resultsColumns.m_indexes];

				// Get the name of the group this should go into
				if (newType == ResultsModelColumns::RESULT_HOST)
				{
					Url urlObj(url);
#ifdef DEBUG
					cout << "ResultsTree::groupBySearchEngine: row " << url << endl;
#endif
					string groupName = urlObj.getHost();
					// Add group
					if (appendGroup(groupName, newType, groupIter) == true)
					{
						// Add result
						success = appendResult(childRow[m_resultsColumns.m_text],
							childRow[m_resultsColumns.m_url],
							(float)atof(from_utf8(childRow[m_resultsColumns.m_score]).c_str()),
							from_utf8(childRow[m_resultsColumns.m_language]),
							childRow[m_resultsColumns.m_rankDiff],
							from_utf8(childRow[m_resultsColumns.m_queryName]),
							engineIds, indexIds, newIter, &(*groupIter), true);
					}
				}
				else
				{
					// Look at the engines column and see which engines this result is for
					set<string> engineNames;
					m_settings.getEngineNames(engineIds, engineNames);
					if (engineNames.empty() == false)
					{
#ifdef DEBUG
						cout << "ResultsTree::groupBySearchEngine: row is for " << engineNames.size() << endl;
#endif
						// Are there indexes in the list ?
						set<string>::iterator xapianIter = engineNames.find("Xapian");
						if ((xapianIter != engineNames.end()) &&
							(indexIds > 0))
						{
							// Erase this
							engineNames.erase(xapianIter);

							// Add entries for each index name so that we can loop once on engine names
							set<string> indexNames;
							m_settings.getIndexNames(indexIds, indexNames);
							for (set<string>::iterator iter = indexNames.begin(); iter != indexNames.end(); ++iter)
							{
								string indexName = (*iter);
								engineNames.insert(indexName);
#ifdef DEBUG
								cout << "ResultsTree::groupBySearchEngine: row is for index " << indexName << endl;
#endif
							}
						}

						for (set<string>::iterator iter = engineNames.begin(); iter != engineNames.end(); ++iter)
						{
							string engineName = (*iter);
							unsigned int indexId = 0;
							unsigned int engineId = m_settings.getEngineId(engineName);

							if (engineId == 0)
							{
								// This is actually an index, not an engine...
								indexId = m_settings.getIndexId(engineName);
								if (indexId > 0)
								{
									engineId = m_settings.getEngineId("Xapian");
								}
							}

							// Add group
							if (appendGroup(engineName, newType, groupIter) == true)
							{
								// Add result
								appendResult(childRow[m_resultsColumns.m_text],
									childRow[m_resultsColumns.m_url],
									(float)atof(from_utf8(childRow[m_resultsColumns.m_score]).c_str()),
									from_utf8(childRow[m_resultsColumns.m_language]),
									childRow[m_resultsColumns.m_rankDiff],
									from_utf8(childRow[m_resultsColumns.m_queryName]),
									engineId, indexId,
									newIter, &(*groupIter), true);
#ifdef DEBUG
								cout << "ResultsTree::groupBySearchEngine: row for " << *iter << endl;
#endif
							}
						}

						// FIXME: make sure at least one row was added
						success = true;
					}
				}

				if (success == true)
				{
					// Delete it
					m_refStore->erase(*childIter);
					childIter = child.begin();
				}
				else
				{
					// Don't delete anything then, just go to the next child
					childIter++;
				}
			}
		}

		// Erase this row
		m_refStore->erase(*iter);

		// Get the new first row, that way we don't have to worry about iterators validity
		iter = children.begin();
	}

	for (std::map<string, TreeModel::iterator>::iterator mapIter = m_resultsGroups.begin();
		mapIter != m_resultsGroups.end(); mapIter++)
	{
		TreeModel::iterator groupIter = mapIter->second;
		updateGroup(groupIter);
	}

	onSelectionChanged();
}
//
// Adds a new row in the results tree.
//
bool ResultsTree::appendResult(const ustring &text, const ustring &url,
	float score, const string &language, int rankDiff,
	const string &queryName, unsigned int engineId, unsigned int indexId,
	TreeModel::iterator &newRowIter, const TreeModel::Row *parentRow, bool noDuplicates)
{
	if (parentRow == NULL)
	{
		newRowIter = m_refStore->append();
	}
	else
	{
		// Merge duplicates within groups ?
		if (noDuplicates == true)
		{
			// Look for a row with the same URL and query. For instance, in group
			// by host mode, if a page is returned by several search engines, it
			// should appear only once
			TreeModel::Children children = parentRow->children();
			if (children.empty() == false)
			{
				TreeModel::Children::iterator childIter = children.begin();
				for (; childIter != children.end(); ++childIter)
				{
					TreeModel::Row row = *childIter;
					if ((row[m_resultsColumns.m_url] == to_utf8(url)) &&
						(row[m_resultsColumns.m_queryName] == to_utf8(queryName)))
					{
						// Update the engines column...
						row[m_resultsColumns.m_engines] = row[m_resultsColumns.m_engines] | engineId;
						// ...and the indexes column too
						row[m_resultsColumns.m_indexes] = row[m_resultsColumns.m_indexes] | engineId;
#ifdef DEBUG
						cout << "ResultsTree::appendResult: merged " << text << " " << engineId << " (" << row[m_resultsColumns.m_engines] << "," << row[m_resultsColumns.m_indexes] << ")" << endl;
#endif

						newRowIter = childIter;
						return true;
					}
				}
			}
		}

		newRowIter = m_refStore->append(parentRow->children());
#ifdef DEBUG
		cout << "ResultsTree::appendResult: added " << text << ", " << score << " to "
			<< (*parentRow)[m_resultsColumns.m_text] << endl;
#endif
	}

	XapianIndex index(m_settings.m_indexLocation);
	ViewHistory viewHistory(m_settings.m_historyDatabase);
	bool isIndexed = false;

	// Is this document indexed ?
	if ((index.isGood() == true) &&
		(index.hasDocument(url) > 0))
	{
		isIndexed = true;
	}

	// Has it been already viewed ?
	bool wasViewed = viewHistory.hasItem(url);

	char scoreStr[128];
	snprintf(scoreStr, 128, "%.f", score);

	TreeModel::Row childRow = *newRowIter;
	updateRow(childRow, text, url, scoreStr,
		to_utf8(language), to_utf8(queryName), engineId, indexId,
		ResultsModelColumns::RESULT_TITLE, isIndexed,
		wasViewed, rankDiff);

	return true;
}