/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "summarywidget.h"
#include <QTimer>
#include <QComboBox>
#include <QMenu>
#include <QToolBar>
#include <QMainWindow>
#include <QWidgetAction>
#include <QCloseEvent>
#include <QSortFilterProxyModel>
#include <QLineEdit>
#include <QtDebug>
#include <interfaces/structures.h>
#include <interfaces/ijobholder.h>
#include <interfaces/core/icoreproxy.h>
#include <interfaces/core/ipluginsmanager.h>
#include <interfaces/core/irootwindowsmanager.h>
#include <interfaces/core/iiconthememanager.h>
#include <interfaces/imwproxy.h>
#include <util/gui/clearlineeditaddon.h>
#include "core.h"
#include "summary.h"
#include "modeldelegate.h"

Q_DECLARE_METATYPE (QMenu*)

#if QT_VERSION < 0x050500
Q_DECLARE_METATYPE (QList<QModelIndex>)
#endif

namespace LeechCraft
{
namespace Summary
{
	QObject *SummaryWidget::S_ParentMultiTabs_ = 0;

	class SearchWidget : public QWidget
	{
		QLineEdit *Edit_;
	public:
		SearchWidget (SummaryWidget *summary)
		: Edit_ (new QLineEdit)
		{
			auto lay = new QHBoxLayout;
			setLayout (lay);

			Edit_->setPlaceholderText (SummaryWidget::tr ("Search..."));
			Edit_->setMaximumWidth (400);
			Edit_->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed);
			lay->addStretch ();
			lay->addWidget (Edit_, 0, Qt::AlignRight);
			new Util::ClearLineEditAddon (Core::Instance ().GetProxy (), Edit_);

			connect (Edit_,
					SIGNAL (textChanged (QString)),
					summary,
					SLOT (filterParametersChanged ()));
			connect (Edit_,
					SIGNAL (returnPressed ()),
					summary,
					SLOT (feedFilterParameters ()));
		}

		QString GetText () const
		{
			return Edit_->text ();
		}

		void SetText (const QString& text)
		{
			Edit_->setText (text);
		}
	};

	SummaryWidget::SummaryWidget (QWidget *parent)
	: QWidget (parent)
	, FilterTimer_ (new QTimer)
	, SearchWidget_ (CreateSearchWidget ())
	, Toolbar_ (new QToolBar)
	, Sorter_ (Core::Instance ().GetTasksModel ())
	{
		Toolbar_->setWindowTitle ("Summary");
		connect (Toolbar_.get (),
				SIGNAL (actionTriggered (QAction*)),
				this,
				SLOT (handleActionTriggered (QAction*)));

		Toolbar_->addWidget (SearchWidget_);

		Ui_.setupUi (this);
		Ui_.PluginsTasksTree_->setItemDelegate (new ModelDelegate (this));

		FilterTimer_->setSingleShot (true);
		FilterTimer_->setInterval (800);
		connect (FilterTimer_,
				SIGNAL (timeout ()),
				this,
				SLOT (feedFilterParameters ()));

		Ui_.ControlsDockWidget_->hide ();

		auto pm = Core::Instance ().GetProxy ()->GetPluginsManager ();
		for (const auto plugin : pm->GetAllCastableRoots<IJobHolder*> ())
			ConnectObject (plugin);

		Ui_.PluginsTasksTree_->setModel (Sorter_);

		connect (Sorter_,
				SIGNAL (dataChanged (QModelIndex, QModelIndex)),
				this,
				SLOT (checkDataChanged (QModelIndex, QModelIndex)));
		connect (Sorter_,
				SIGNAL (modelAboutToBeReset ()),
				this,
				SLOT (handleReset ()));
		connect (Sorter_,
				SIGNAL (rowsAboutToBeRemoved (QModelIndex, int, int)),
				this,
				SLOT (checkRowsToBeRemoved (QModelIndex, int, int)));
		connect (Ui_.PluginsTasksTree_->selectionModel (),
				SIGNAL (currentRowChanged (QModelIndex, QModelIndex)),
				this,
				SLOT (updatePanes (QModelIndex, QModelIndex)));
		connect (Ui_.PluginsTasksTree_->selectionModel (),
				SIGNAL (currentRowChanged (QModelIndex, QModelIndex)),
				this,
				SLOT (syncSelection (QModelIndex)),
				Qt::QueuedConnection);

		const auto itemsHeader = Ui_.PluginsTasksTree_->header ();
		const auto& fm = fontMetrics ();
		itemsHeader->resizeSection (0,
				fm.width ("Average download job or torrent name is just like this."));
		itemsHeader->resizeSection (1,
				fm.width ("Of the download."));
		itemsHeader->resizeSection (2,
				fm.width ("99.99% (1024.0 kb from 1024.0 kb at 1024.0 kb/s)"));

		ReconnectModelSpecific ();
	}

	void SummaryWidget::ReconnectModelSpecific ()
	{
		const auto sel = Ui_.PluginsTasksTree_->selectionModel ();

#define C2(sig,sl,arg1,arg2) \
		if (mo->indexOfMethod (QMetaObject::normalizedSignature ("handleTasksTreeSelection" #sl "(" #arg1 ", " #arg2 ")")) != -1) \
			connect (sel, \
					SIGNAL (sig (arg1, arg2)), \
					object, \
					SLOT (handleTasksTreeSelection##sl (arg1, arg2)));

		auto pm = Core::Instance ().GetProxy ()->GetPluginsManager ();
		Q_FOREACH (QObject *object, pm->GetAllCastableRoots<IJobHolder*> ())
		{
			const auto *mo = object->metaObject ();

			C2 (currentChanged, CurrentChanged, QModelIndex, QModelIndex);
			C2 (currentColumnChanged, CurrentColumnChanged, QModelIndex, QModelIndex);
			C2 (currentRowChanged, CurrentRowChanged, QModelIndex, QModelIndex);
		}
#undef C2
	}

	void SummaryWidget::ConnectObject (QObject *object)
	{
		const auto *mo = object->metaObject ();

#define C1(sig,sl,arg) \
		if (mo->indexOfMethod (QMetaObject::normalizedSignature ("handleTasksTree" #sl "(" #arg ")")) != -1) \
			connect (Ui_.PluginsTasksTree_, \
					SIGNAL (sig (arg)), \
					object, \
					SLOT (handleTasksTree##sl (arg)));

		C1 (activated, Activated, QModelIndex);
		C1 (clicked, Clicked, QModelIndex);
		C1 (doubleClicked, DoubleClicked, QModelIndex);
		C1 (entered, Entered, QModelIndex);
		C1 (pressed, Pressed, QModelIndex);
		C1 (viewportEntered, ViewportEntered, );
#undef C1
	}

	SummaryWidget::~SummaryWidget ()
	{
		Toolbar_->clear ();

		QWidget *widget = Ui_.ControlsDockWidget_->widget ();
		Ui_.ControlsDockWidget_->setWidget (0);
		if (widget)
			widget->setParent (0);

		delete Sorter_;
	}

	void SummaryWidget::SetParentMultiTabs (QObject *parent)
	{
		S_ParentMultiTabs_ = parent;
	}

	void SummaryWidget::Remove ()
	{
		emit needToClose ();
	}

	QToolBar* SummaryWidget::GetToolBar () const
	{
		return Toolbar_.get ();
	}

	QList<QAction*> SummaryWidget::GetTabBarContextMenuActions () const
	{
		return QList<QAction*> ();
	}

	QObject* SummaryWidget::ParentMultiTabs ()
	{
		return S_ParentMultiTabs_;
	}

	TabClassInfo SummaryWidget::GetTabClassInfo () const
	{
		return qobject_cast<Summary*> (S_ParentMultiTabs_)->GetTabClasses ().first ();
	}

	SearchWidget* SummaryWidget::CreateSearchWidget ()
	{
		return new SearchWidget (this);
	}

	void SummaryWidget::ReinitToolbar ()
	{
		Q_FOREACH (QAction *action, Toolbar_->actions ())
		{
			auto wa = qobject_cast<QWidgetAction*> (action);
			if (!wa || wa->defaultWidget () != SearchWidget_)
			{
				Toolbar_->removeAction (action);
				delete action;
			}
			else if (wa->defaultWidget () != SearchWidget_)
				Toolbar_->removeAction (action);
		}
	}

	QList<QAction*> SummaryWidget::CreateProxyActions (const QList<QAction*>& actions, QObject *parent) const
	{
		QList<QAction*> proxies;

		Q_FOREACH (QAction *action, actions)
		{
			if (qobject_cast<QWidgetAction*> (action))
			{
				proxies << action;
				continue;
			}

			QAction *pa = new QAction (action->icon (), action->text (), parent);
			if (action->isSeparator ())
				pa->setSeparator (true);
			else
			{
				pa->setCheckable (action->isCheckable ());
				pa->setChecked (action->isChecked ());
				pa->setShortcuts (action->shortcuts ());
				pa->setStatusTip (action->statusTip ());
				pa->setToolTip (action->toolTip ());
				pa->setWhatsThis (action->whatsThis ());
				pa->setData (QVariant::fromValue<QObject*> (action));

				connect (pa,
						SIGNAL (hovered ()),
						action,
						SIGNAL (hovered ()));
				connect (pa,
						SIGNAL (toggled (bool)),
						action,
						SIGNAL (toggled (bool)));
			}
			proxies << pa;
		}

		return proxies;
	}

	QByteArray SummaryWidget::GetTabRecoverData () const
	{
		QByteArray result;
		QDataStream out (&result, QIODevice::WriteOnly);
		out << static_cast<quint8> (1);
		return result;
	}

	QString SummaryWidget::GetTabRecoverName () const
	{
		return GetTabClassInfo ().VisibleName_;
	}

	QIcon SummaryWidget::GetTabRecoverIcon () const
	{
		return GetTabClassInfo ().Icon_;
	}

	void SummaryWidget::RestoreState (const QByteArray& data)
	{
		QDataStream in (data);
		quint8 version = 0;
		in >> version;
		if (version != 1)
		{
			qWarning () << Q_FUNC_INFO
					<< "unknown version";
			return;
		}
	}

	void SummaryWidget::SetUpdatesEnabled (bool)
	{
		// TODO implement this
	}

	Ui::SummaryWidget SummaryWidget::GetUi () const
	{
		return Ui_;
	}

	void SummaryWidget::handleActionTriggered (QAction *proxyAction)
	{
		QAction *action = qobject_cast<QAction*> (proxyAction->
				data ().value<QObject*> ());
		QItemSelectionModel *selModel =
				Ui_.PluginsTasksTree_->selectionModel ();
		QModelIndexList indexes = selModel->selectedRows ();
		action->setProperty ("SelectedRows",
				QVariant::fromValue<QList<QModelIndex>> (indexes));
		action->setProperty ("ItemSelectionModel",
				QVariant::fromValue<QObject*> (selModel));

		action->activate (QAction::Trigger);
	}

	void SummaryWidget::checkDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
	{
		const QModelIndex& cur = Ui_.PluginsTasksTree_->
				selectionModel ()->currentIndex ();
		if (topLeft.row () <= cur.row () && bottomRight.row () >= cur.row ())
			updatePanes (cur, cur);
	}

	void SummaryWidget::handleReset ()
	{
		Ui_.PluginsTasksTree_->selectionModel ()->clear ();
	}

	void SummaryWidget::checkRowsToBeRemoved (const QModelIndex&, int begin, int end)
	{
		const QModelIndex& cur = Ui_.PluginsTasksTree_->
				selectionModel ()->currentIndex ();
		if (begin <= cur.row () && end >= cur.row ())
			Ui_.PluginsTasksTree_->selectionModel ()->clear ();
	}

	void SummaryWidget::updatePanes (const QModelIndex& newIndex, const QModelIndex& oldIndex)
	{
		QToolBar *controls = Core::Instance ().GetControls (newIndex);
		QWidget *addiInfo = Core::Instance ().GetAdditionalInfo (newIndex);

		if (oldIndex.isValid () &&
				addiInfo != Ui_.ControlsDockWidget_->widget ())
			Ui_.ControlsDockWidget_->hide ();

		if (Core::Instance ().SameModel (newIndex, oldIndex))
			return;

		ReinitToolbar ();
		if (newIndex.isValid ())
		{
			if (controls)
			{
				Q_FOREACH (QAction *action, controls->actions ())
				{
					QString ai = action->property ("ActionIcon").toString ();
					if (!ai.isEmpty () &&
							action->icon ().isNull ())
						action->setIcon (Core::Instance ().GetProxy ()->
									GetIconThemeManager ()->GetIcon (ai));
				}

				const auto& proxies = CreateProxyActions (controls->actions (), Toolbar_.get ());
				Toolbar_->insertActions (Toolbar_->actions ().first (), proxies);
			}
			if (addiInfo != Ui_.ControlsDockWidget_->widget ())
				Ui_.ControlsDockWidget_->setWidget (addiInfo);

			if (addiInfo)
			{
				Ui_.ControlsDockWidget_->show ();
				Core::Instance ().GetProxy ()->GetIconThemeManager ()->
						UpdateIconset (addiInfo->findChildren<QAction*> ());
			}
		}
	}

	void SummaryWidget::filterParametersChanged ()
	{
		FilterTimer_->stop ();
		FilterTimer_->start ();
	}

	void SummaryWidget::filterReturnPressed ()
	{
		FilterTimer_->stop ();
		feedFilterParameters ();
	}

	void SummaryWidget::feedFilterParameters ()
	{
		Sorter_->setFilterFixedString (SearchWidget_->GetText ());
	}

	void SummaryWidget::on_PluginsTasksTree__customContextMenuRequested (const QPoint& pos)
	{
		QModelIndex current = Ui_.PluginsTasksTree_->currentIndex ();
		QMenu *sourceMenu = current.data (RoleContextMenu).value<QMenu*> ();
		if (!sourceMenu)
			return;

		QMenu *menu = new QMenu ();
		connect (menu,
				SIGNAL (triggered (QAction*)),
				this,
				SLOT (handleActionTriggered (QAction*)));
		menu->setAttribute (Qt::WA_DeleteOnClose, true);
		menu->addActions (CreateProxyActions (sourceMenu->actions (), menu));
		menu->setTitle (sourceMenu->title ());
		menu->popup (Ui_.PluginsTasksTree_->viewport ()->mapToGlobal (pos));
	}

	void SummaryWidget::syncSelection (const QModelIndex& current)
	{
		QItemSelectionModel *selm = Ui_.PluginsTasksTree_->selectionModel ();
		const QModelIndex& now = selm->currentIndex ();
		if (current != now ||
				(now.isValid () &&
					!selm->rowIntersectsSelection (now.row (), QModelIndex ())))
		{
			selm->select (now, QItemSelectionModel::ClearAndSelect |
					QItemSelectionModel::Rows);
			updatePanes (now, current);
		}
	}
}
}
