/***************************************************************************
              searchspymodel.cpp  -  Search Spy Model implementation
                             -------------------
    begin                : Sat Nov 3 2007
    copyright            : (C) 2007 by Edward Sheldrake
    email                : ejs1920@yahoo.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <QTime>
#include <QtAlgorithms>

#include "searchspymodel.h"

/** Initialises values */
SearchSpyItem::SearchSpyItem( QString search )
{
	text = search;
	count = 1;
	time = QTime::currentTime().toString("hh:mm:ss");
	
	// invalid initial value
	index = -1;
}

/** */
SearchSpyItem::~SearchSpyItem()
{

}

/** Increment count, update time */
void SearchSpyItem::hit()
{
	count++;
	time = QTime::currentTime().toString("hh:mm:ss");
}

/** */
SearchSpyModel::SearchSpyModel() : QAbstractItemModel()
{
	sortColumn = 0;
	sortOrder = Qt::AscendingOrder;
}

/** delete all items in model */
SearchSpyModel::~SearchSpyModel()
{
	clear();
}

/** */
int SearchSpyModel::rowCount( const QModelIndex & /* index */ ) const
{
	return itemList.size();
}

/** */
int SearchSpyModel::columnCount( const QModelIndex & /* index */ ) const
{
	return 3;
}

/** */
QVariant SearchSpyModel::data( const QModelIndex & index, int role ) const
{
	if ( index.isValid() ) 
	{
		SearchSpyItem * item = itemList[index.row()];
		
		if ( item != 0 )
		{
			if ( role == Qt::DisplayRole )
			{
				switch ( index.column() )
				{
					case 0:	return item->text;
					case 1: return item->count;
					case 2: return item->time;
				}
			}
			else if ( role == Qt::TextAlignmentRole )
			{
				if ( index.column() == 1 )
				{
					return Qt::AlignRight;
				}
			}
		}
	}
	
	return QVariant();
}

/** */
QVariant SearchSpyModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
	if ( (orientation == Qt::Horizontal) && (role == Qt::DisplayRole) )
	{
		switch ( section )
		{
			case 0: return tr("Text");
			case 1: return tr("Count");
			case 2: return tr("Time");
		}
	}

	return QVariant();
}

/** */
bool SpyItemQueryLessThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->text < r->text );
}

/** */
bool SpyItemCountLessThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->count < r->count );
}

/** */
bool SpyItemTimeLessThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->time < r->time );
}

/** */
bool SpyItemQueryGreaterThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->text > r->text );
}

/** */
bool SpyItemCountGreaterThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->count > r->count );
}

/** */
bool SpyItemTimeGreaterThan( const SearchSpyItem * l, const SearchSpyItem * r )
{
	return ( l->time > r->time );
}

/** */
void SearchSpyModel::sort( int column, Qt::SortOrder order )
{
	emit layoutAboutToBeChanged();
	
	// save vales so we can insert new items at correct place
	sortColumn = column;
	sortOrder = order;
	
	if ( order == Qt::AscendingOrder )
	{
		switch ( column )
		{
			case 0:	qSort( itemList.begin(), itemList.end(), SpyItemQueryLessThan );
				break;
			case 1: qSort( itemList.begin(), itemList.end(), SpyItemCountLessThan );
				break;
			case 2: qSort( itemList.begin(), itemList.end(), SpyItemTimeLessThan );
				break;
		}
	}
	else if ( order == Qt::DescendingOrder )
	{
		switch ( column )
		{
			case 0: qSort( itemList.begin(), itemList.end(), SpyItemQueryGreaterThan );
				break;
			case 1: qSort( itemList.begin(), itemList.end(), SpyItemCountGreaterThan );
				break;
			case 2: qSort( itemList.begin(), itemList.end(), SpyItemTimeGreaterThan );
				break;
		}
	}
	
	for ( int i = 0; i < itemList.size(); i++ )
	{
		(itemList.at(i))->index = i;
	}
	
	emit layoutChanged();
}

/** */
QModelIndex SearchSpyModel::index( int row, int column, const QModelIndex & parent ) const
{
	if ( !hasIndex( row, column, parent ) )
	{
		return QModelIndex();
	}
	
	return createIndex( row, column );
}

/** */
QModelIndex SearchSpyModel::parent( const QModelIndex & /* parent */ ) const
{
	return QModelIndex();
}

/** */
void SearchSpyModel::search( QString query )
{
	if ( itemMap.contains( query ) )
	{
		SearchSpyItem * item = itemMap.value( query );
		item->hit();
		
		QModelIndex topLeft = createIndex( item->index, 1 );
		QModelIndex bottomRight = createIndex( item->index, 2 );
		
		emit dataChanged( topLeft, bottomRight );
	}
	else
	{
		emit layoutAboutToBeChanged();
		
		SearchSpyItem * item = new SearchSpyItem( query );
		itemMap[ query ] = item;
		
		// find position in list for new item
		int i = 0;
		
		if ( sortOrder == Qt::AscendingOrder )
		{
			switch ( sortColumn )
			{
				case 0:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->text < (itemList.at(i))->text )
						{
							break;
						}
					}
					break;
				case 1:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->count < (itemList.at(i))->count )
						{
							break;
						}
					}
					break;
				case 2:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->time < (itemList.at(i))->time )
						{
							break;
						}
					}
					break;
			}
		}
		else if ( sortOrder == Qt::DescendingOrder )
		{
			switch ( sortColumn )
			{
				case 0:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->text > (itemList.at(i))->text )
						{
							break;
						}
					}
					break;
				case 1:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->count > (itemList.at(i))->count )
						{
							break;
						}
					}
					break;
				case 2:	for ( i = 0; i < itemList.size(); i++ )
					{
						if ( item->time > (itemList.at(i))->time )
						{
							break;
						}
					}
					break;
			}
		}
		
		item->index = i;
		itemList.insert( i, item );
		
		i++;
		
		// fix indexes
		for ( ; i < itemList.size(); i++ )
		{
			(itemList.at(i))->index++;
		}
		
		emit layoutChanged();
	}
}

/** */
void SearchSpyModel::clear()
{
	emit layoutAboutToBeChanged();
	
	for ( int i = 0; i < itemList.size(); i++ )
	{
		delete itemList.at(i);
	}
	
	itemList.clear();
	itemMap.clear();
	
	emit layoutChanged();
}
