/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2012  Georg Rudoy
 *
 * 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/

#include "itemslistmodel.h"
#include <algorithm>
#include <QApplication>
#include <QPalette>
#include <QTextDocument>
#include <QtDebug>
#include "core.h"
#include "xmlsettingsmanager.h"

namespace LeechCraft
{
namespace Aggregator
{
	ItemsListModel::ItemsListModel (QObject *parent)
	: QAbstractItemModel (parent)
	, CurrentRow_ (-1)
	, CurrentChannel_ (-1)
	, MayBeRichText_ (false)
	{
		ItemHeaders_ << tr ("Name") << tr ("Date");
		connect (&Core::Instance (),
				SIGNAL (channelRemoved (IDType_t)),
				this,
				SLOT (handleChannelRemoved (IDType_t)));
	}

	int ItemsListModel::GetSelectedRow () const
	{
		return CurrentRow_;
	}

	const IDType_t& ItemsListModel::GetCurrentChannel () const
	{
		return CurrentChannel_;
	}

	void ItemsListModel::SetCurrentChannel (const IDType_t& channel)
	{
		CurrentChannel_ = channel;
		reset ();
	}

	void ItemsListModel::Selected (const QModelIndex& index)
	{
		CurrentRow_ = index.row ();
		if (!index.isValid ())
			return;

		ItemShort item = CurrentItems_ [CurrentRow_];
		item.Unread_ = false;
		Core::Instance ().GetStorageBackend ()->UpdateItem (item);
	}

	void ItemsListModel::MarkItemReadStatus (const QModelIndex& i, bool read)
	{
		ItemShort is = CurrentItems_ [i.row ()];
		is.Unread_ = !read;
		Core::Instance ().GetStorageBackend ()->UpdateItem (is);
	}

	const ItemShort& ItemsListModel::GetItem (const QModelIndex& index) const
	{
		return CurrentItems_ [index.row ()];
	}

	bool ItemsListModel::IsItemRead (int item) const
	{
		return !CurrentItems_ [item].Unread_;
	}

	QStringList ItemsListModel::GetCategories (int item) const
	{
		return CurrentItems_ [item].Categories_;
	}

	void ItemsListModel::Reset (const IDType_t& channel)
	{
		CurrentChannel_ = channel;
		CurrentRow_ = -1;
		CurrentItems_.clear ();
		if (channel != static_cast<IDType_t> (-1))
		{
			Core::Instance ().GetStorageBackend ()->
				GetItems (CurrentItems_, channel);
			if (!CurrentItems_.empty ())
				MayBeRichText_ = Qt::mightBeRichText (CurrentItems_.at (0).Title_);
		}
		reset ();
	}

	void ItemsListModel::Reset (const QList<IDType_t>& items)
	{
		CurrentChannel_ = -1;
		CurrentRow_ = -1;
		CurrentItems_.clear ();

		StorageBackend *sb = Core::Instance ().GetStorageBackend ();
		Q_FOREACH (const IDType_t& itemId, items)
			CurrentItems_.push_back (sb->GetItem (itemId)->ToShort ());

		if (!CurrentItems_.empty ())
			MayBeRichText_ = Qt::mightBeRichText (CurrentItems_.at (0).Title_);

		reset ();
	}

	void ItemsListModel::RemoveItems (QSet<IDType_t> ids)
	{
		if (ids.isEmpty ())
			return;

		const bool shouldReset = ids.size () > 10;

		for (auto i = CurrentItems_.begin ();
				i != CurrentItems_.end () && !ids.isEmpty (); )
		{
			if (!ids.contains (i->ItemID_))
			{
				++i;
				continue;
			}

			ids.remove (i->ItemID_);
			if (!shouldReset)
			{
				const size_t dist = std::distance (CurrentItems_.begin (), i);
				beginRemoveRows (QModelIndex (), dist, dist);
			}
			i = CurrentItems_.erase (i);
			if (!shouldReset)
			{
				endRemoveRows ();
				qApp->processEvents (QEventLoop::ExcludeUserInputEvents);
			}
		}

		if (shouldReset)
			reset ();
	}

	void ItemsListModel::ItemDataUpdated (Item_ptr item)
	{
		ItemShort is = item->ToShort ();

		auto pos = CurrentItems_.end ();

		for (auto i = CurrentItems_.begin (),
				end = CurrentItems_.end (); i != end; ++i)
			if (is.Title_ == i->Title_ &&
					is.URL_ == i->URL_)
			{
				pos = i;
				break;
			}

		// Item is new
		if (pos == CurrentItems_.end ())
		{
			auto insertPos = std::find_if (CurrentItems_.begin (), CurrentItems_.end (),
						[item] (const ItemShort& is) { return item->PubDate_ > is.PubDate_; });

			int shift = std::distance (CurrentItems_.begin (), insertPos);

			beginInsertRows (QModelIndex (), shift, shift);
			CurrentItems_.insert (insertPos, is);
			endInsertRows ();
		}
		// Item exists already
		else
		{
			*pos = is;

			int distance = std::distance (CurrentItems_.begin (), pos);
			emit dataChanged (index (distance, 0), index (distance, 1));
		}
	}

	int ItemsListModel::columnCount (const QModelIndex&) const
	{
		return ItemHeaders_.size ();
	}

	QVariant ItemsListModel::data (const QModelIndex& index, int role) const
	{
		if (!index.isValid () || index.row () >= rowCount ())
			return QVariant ();

		if (role == Qt::DisplayRole)
		{
			switch (index.column ())
			{
				case 0:
					{
						QString title = CurrentItems_ [index.row ()].Title_;
						if (MayBeRichText_ &&
								Qt::mightBeRichText (title))
						{
							QTextDocument doc;
							doc.setHtml (title);
							title = doc.toPlainText ();
						}
						return title;
					}
				case 1:
					return CurrentItems_ [index.row ()].PubDate_;
				default:
					return QVariant ();
			}
		}
		//Color mark an items as read/unread
		else if (role == Qt::ForegroundRole)
		{
			bool palette = XmlSettingsManager::Instance ()->
					property ("UsePaletteColors").toBool ();
			if (CurrentItems_ [index.row ()].Unread_)
			{
				if (XmlSettingsManager::Instance ()->
						property ("UnreadCustomColor").toBool ())
					return XmlSettingsManager::Instance ()->
							property ("UnreadItemsColor").value<QColor> ();
				else
					return palette ?
						QApplication::palette ().link ().color () :
						QVariant ();
			}
			else
				return palette ?
					QApplication::palette ().linkVisited ().color () :
					QVariant ();
		}
		else if (role == Qt::FontRole &&
				CurrentItems_ [index.row ()].Unread_)
			return XmlSettingsManager::Instance ()->
				property ("UnreadItemsFont");
		else if (role == Qt::ToolTipRole &&
				XmlSettingsManager::Instance ()->property ("ShowItemsTooltips").toBool ())
		{
			IDType_t id = CurrentItems_ [index.row ()].ItemID_;
			Item_ptr item = Core::Instance ()
					.GetStorageBackend ()->GetItem (id);
			QString result = QString ("<qt><strong>%1</strong><br />").arg (item->Title_);
			if (item->Author_.size ())
			{
				result += tr ("<b>Author</b>: %1").arg (item->Author_);
				result += "<br />";
			}
			if (item->Categories_.size ())
			{
				result += tr ("<b>Categories</b>: %1").arg (item->Categories_.join ("; "));
				result += "<br />";
			}
			if (item->NumComments_ > 0)
			{
				result += tr ("%n comment(s)", "", item->NumComments_);
				result += "<br />";
			}
			if (item->Enclosures_.size () > 0)
			{
				result += tr ("%n enclosure(s)", "", item->Enclosures_.size ());
				result += "<br />";
			}
			if (item->MRSSEntries_.size () > 0)
			{
				result += tr ("%n MediaRSS entry(s)", "", item->MRSSEntries_.size ());
				result += "<br />";
			}
			if (item->CommentsLink_.size ())
			{
				result += tr ("RSS with comments is available");
				result += "<br />";
			}
			result += "<br />";
			const int maxDescriptionSize = 1000;
			result += item->Description_.left (maxDescriptionSize);
			if (item->Description_.size () > maxDescriptionSize)
				result += "...";
			return result;
		}
		else if (role == Qt::BackgroundRole)
		{
			const QPalette& p = QApplication::palette ();
			QLinearGradient grad (0, 0, 0, 10);
			grad.setColorAt (0, p.color (QPalette::AlternateBase));
			grad.setColorAt (1, p.color (QPalette::Base));
			return QBrush (grad);
		}
		else
			return QVariant ();
	}

	Qt::ItemFlags ItemsListModel::flags (const QModelIndex&) const
	{
		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	}

	QVariant ItemsListModel::headerData (int column, Qt::Orientation orient, int role) const
	{
		if (orient == Qt::Horizontal && role == Qt::DisplayRole)
			return ItemHeaders_.at (column);
		else
			return QVariant ();
	}

	QModelIndex ItemsListModel::index (int row, int column, const QModelIndex& parent) const
	{
		if (!hasIndex (row, column, parent))
			return QModelIndex ();

		return createIndex (row, column);
	}

	QModelIndex ItemsListModel::parent (const QModelIndex&) const
	{
		return QModelIndex ();
	}

	int ItemsListModel::rowCount (const QModelIndex& parent) const
	{
		return parent.isValid () ? 0 : CurrentItems_.size ();
	}

	void ItemsListModel::handleChannelRemoved (IDType_t id)
	{
		if (id != CurrentChannel_)
			return;
		Reset (-1);
	}
}
}
