/**********************************************************************
 * 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 "upmanager.h"
#include <QApplication>
#include <QClipboard>
#include <QStandardItemModel>
#include <QFileInfo>
#include <interfaces/structures.h>
#include <interfaces/ijobholder.h>
#include <interfaces/core/ientitymanager.h>
#include <util/util.h>
#include <util/xpc/util.h>
#include "interfaces/netstoremanager/istorageaccount.h"
#include "interfaces/netstoremanager/istorageplugin.h"
#include "interfaces/netstoremanager/isupportfilelistings.h"
#include "xmlsettingsmanager.h"

inline uint qHash (const QStringList& id)
{
	return qHash (id.join ("/"));
}

namespace LeechCraft
{
namespace NetStoreManager
{
	UpManager::UpManager (ICoreProxy_ptr proxy, QObject *parent)
	: QObject (parent)
	, ReprModel_ (new QStandardItemModel (0, 3, this))
	, Proxy_ (proxy)
	{
	}

	QAbstractItemModel* UpManager::GetRepresentationModel () const
	{
		return ReprModel_;
	}

	void UpManager::RemovePending (const QString& path)
	{
		IStorageAccount *acc = qobject_cast<IStorageAccount*> (sender ());
		Uploads_ [acc].removeAll (path);

		auto items = ReprItems_ [acc].take (path);
		if (items.isEmpty ())
		{
			qWarning () << Q_FUNC_INFO
					<< "empty items list for"
					<< path;
			return;
		}
		ReprModel_->removeRow (items.first ()->row ());
	}

	IStoragePlugin* UpManager::GetSenderPlugin ()
	{
		IStorageAccount *acc = qobject_cast<IStorageAccount*> (sender ());
		return qobject_cast<IStoragePlugin*> (acc->GetParentPlugin ());
	}

	void UpManager::ScheduleAutoshare (const QString& path)
	{
		Autoshare_ << path;
	}

	void UpManager::handleUploadRequest (IStorageAccount *acc, const QString& path,
			const QByteArray& id, bool byHand)
	{
		if (!Uploads_.contains (acc))
		{
			QObject *accObj = acc->GetQObject ();
			connect (accObj,
					SIGNAL (upError (QString, QString)),
					this,
					SLOT (handleError (QString, QString)));
			connect (accObj,
					SIGNAL (upStatusChanged (QString, QString)),
					this,
					SLOT (handleUpStatusChanged (QString, QString)));
			connect (accObj,
					SIGNAL (upFinished (QByteArray, QString)),
					this,
					SLOT (handleUpFinished (QByteArray, QString)));
			connect (accObj,
					SIGNAL (upProgress (quint64, quint64, QString)),
					this,
					SLOT (handleUpProgress (quint64, quint64, QString)));

			if (qobject_cast<ISupportFileListings*> (accObj))
				connect (accObj,
						SIGNAL (gotFileUrl (QUrl, QByteArray)),
						this,
						SLOT (handleGotURL (QUrl, QByteArray)));
		}
		else if (Uploads_ [acc].contains (path))
		{
			const Entity& e = Util::MakeNotification ("NetStoreManager",
					tr ("%1 is already uploading to %2.")
						.arg (QFileInfo (path).fileName ())
						.arg (acc->GetAccountName ()),
					PWarning_);
			Proxy_->GetEntityManager ()->HandleEntity (e);
			return;
		}

		acc->Upload (path, id);

		auto plugin = qobject_cast<IStoragePlugin*> (acc->GetParentPlugin ());

		const QFileInfo fi (path);
		QList<QStandardItem*> row
		{
			new QStandardItem (tr ("Uploading %1 to %2...")
						.arg (fi.fileName ())
						.arg (plugin->GetStorageName ())),
			new QStandardItem (),
			new QStandardItem (tr ("Initializing..."))
		};

		auto progressItem = row.at (JobHolderColumn::JobProgress);
		progressItem->setData (QVariant::fromValue<JobHolderRow> (JobHolderRow::ProcessProgress),
				CustomDataRoles::RoleJobHolderRow);
		progressItem->setData (QVariant::fromValue<ProcessStateInfo> ({
					0,
					fi.size (),
					FromUserInitiated
				}),
				JobHolderRole::ProcessState);
		ReprModel_->appendRow (row);

		ReprItems_ [acc] [path] = row;

		if (byHand &&
				XmlSettingsManager::Instance ().Property ("CopyUrlOnUpload", false).toBool ())
			ScheduleAutoshare (path);
	}

	void UpManager::handleGotURL (const QUrl& url, const QByteArray& id)
	{
		const auto& handlers = URLHandlers_.take (id);
		if (!handlers.isEmpty ())
		{
			Q_FOREACH (auto handler, handlers)
				handler (url, id);

			return;
		}

		const QString& urlStr = url.toString ();
		qApp->clipboard ()->setText (urlStr, QClipboard::Clipboard);
		qApp->clipboard ()->setText (urlStr, QClipboard::Selection);

		auto plugin = GetSenderPlugin ();
		const Entity& e = Util::MakeNotification (plugin->GetStorageName (),
				tr ("URL is pasted into clipboard."),
				PInfo_);
		Proxy_->GetEntityManager ()->HandleEntity (e);
	}

	void UpManager::handleError (const QString& str, const QString& path)
	{
		qWarning () << Q_FUNC_INFO << str << path;

		RemovePending (path);

		auto plugin = GetSenderPlugin ();
		const Entity& e = Util::MakeNotification (plugin->GetStorageName (),
				tr ("Failed to upload %1: %2.")
					.arg (path)
					.arg (str),
				PWarning_);
		Proxy_->GetEntityManager ()->HandleEntity (e);
	}

	void UpManager::handleUpStatusChanged (const QString& status, const QString& filepath)
	{
		IStorageAccount *acc = qobject_cast<IStorageAccount*> (sender ());
		const auto& list = ReprItems_ [acc] [filepath];
		if (list.isEmpty ())
			return;
		list [1]->setText (status);
	}

	void UpManager::handleUpFinished (const QByteArray& id, const QString& filePath)
	{
		RemovePending (filePath);
		const auto& e =Util::MakeNotification ("NetStoreManager",
				tr ("File %1 was uploaded successfully")
						.arg ("<em>" + QFileInfo (filePath).fileName () + "</em>"),
				PInfo_);
		Proxy_->GetEntityManager ()->HandleEntity (e);

		URLHandlers_ [id] << [this, filePath] (const QUrl& url, const QByteArray&)
		{
			emit fileUploaded (filePath, url);
		};

		if (Autoshare_.remove (filePath))
		{
			auto ifl = qobject_cast<ISupportFileListings*> (sender ());
			if (!ifl)
			{
				qWarning () << Q_FUNC_INFO
						<< "account doesn't support file listings, cannot autoshare";
				return;
			}

			ifl->RequestUrl (id);
		}
	}

	void UpManager::handleUpProgress (quint64 done, quint64 total, const QString& filepath)
	{
		IStorageAccount *acc = qobject_cast<IStorageAccount*> (sender ());
		const auto& list = ReprItems_ [acc] [filepath];
		if (list.isEmpty ())
			return;

		auto item = list.at (2);
		item->setText (tr ("%1 of %2")
				.arg (Util::MakePrettySize (done))
				.arg (Util::MakePrettySize (total)));
		Util::SetJobHolderProgress (item, done, total);
	}
}
}
