/**********************************************************************
 * 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 "uploadmanager.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QHttpMultiPart>
#include <QFileInfo>
#include <QFile>
#include <QDomDocument>
#include <QtDebug>
#include <qjson/parser.h>
#include <util/sll/queuemanager.h>
#include "vkaccount.h"

namespace LeechCraft
{
namespace Blasq
{
namespace Rappor
{
	UploadManager::UploadManager (Util::QueueManager *reqQueue, ICoreProxy_ptr proxy, VkAccount *acc)
	: QObject (acc)
	, Acc_ (acc)
	, Proxy_ (proxy)
	, RequestQueue_ (reqQueue)
	{
	}

	void UploadManager::Upload (const QString& aidStr, const QList<UploadItem>& items)
	{
		Acc_->Schedule ([this, items, aidStr] (const QString& authKey) -> void
			{
				QUrl getUrl ("https://api.vk.com/method/photos.getUploadServer.xml");
				getUrl.addQueryItem ("aid", aidStr);
				getUrl.addQueryItem ("access_token", authKey);
				RequestQueue_->Schedule ([this, getUrl, items] () -> void
					{
						auto reply = Proxy_->GetNetworkAccessManager ()->
								get (QNetworkRequest (getUrl));
						connect (reply,
								SIGNAL (finished ()),
								this,
								SLOT (handlePhotosUploadServer ()));
						PhotosUploadServer2Infos_ [reply] = items;
					}, this);
			});
	}

	void UploadManager::StartUpload (const QString& server, QList<UploadItem> infos)
	{
		if (infos.isEmpty ())
			return;

		const auto& info = infos.takeFirst ();

		auto multipart = new QHttpMultiPart (QHttpMultiPart::FormDataType);

		const auto& path = info.FilePath_;

		auto file = new QFile (path, multipart);
		file->open (QIODevice::ReadOnly);

		QHttpPart filePart;

		const auto& disp = QString ("form-data; name=\"file1\"; filename=\"%1\"")
				.arg (QFileInfo (path).fileName ());
		filePart.setHeader (QNetworkRequest::ContentDispositionHeader, disp);

		filePart.setBodyDevice (file);

		multipart->append (filePart);

		const auto nam = Proxy_->GetNetworkAccessManager ();
		auto reply = nam->post (QNetworkRequest (QUrl (server)), multipart);
		connect (reply,
				SIGNAL (finished ()),
				this,
				SLOT (handlePhotosUploaded ()));
		connect (reply,
				SIGNAL (uploadProgress (qint64, qint64)),
				this,
				SLOT (handlePhotosUploadProgress (qint64, qint64)));
		PhotoUpload2Info_ [reply] = info;
		PhotoUpload2QueueTail_ [reply] = infos;
		PhotoUpload2Server_ [reply] = server;
		multipart->setParent (reply);
	}

	void UploadManager::handlePhotosUploadServer ()
	{
		auto reply = qobject_cast<QNetworkReply*> (sender ());
		reply->deleteLater ();

		const auto& data = reply->readAll ();
		QDomDocument doc;
		if (!doc.setContent (data))
		{
			qWarning () << Q_FUNC_INFO
					<< "cannot parse reply"
					<< data;
			return;
		}

		const auto& server = doc.documentElement ().firstChildElement ("upload_url").text ();
		StartUpload (server, PhotosUploadServer2Infos_.take (reply));
	}

	void UploadManager::handlePhotosUploadProgress (qint64 done, qint64 total)
	{
		qDebug () << "upload" << done << total;
	}

	void UploadManager::handlePhotosUploaded ()
	{
		auto reply = qobject_cast<QNetworkReply*> (sender ());
		reply->deleteLater ();

		const auto& tail = PhotoUpload2QueueTail_.take (reply);
		const auto& server = PhotoUpload2Server_.take (reply);
		StartUpload (server, tail);

		const auto& data = reply->readAll ();
		const auto& parsed = QJson::Parser ().parse (data).toMap ();

		const auto& info = PhotoUpload2Info_.take (reply);

		Acc_->Schedule ([this, parsed, info] (const QString& authKey) -> void
			{
				QUrl saveUrl ("https://api.vk.com/method/photos.save.xml");
				auto add = [&saveUrl, &parsed] (const QString& name)
					{ saveUrl.addQueryItem (name, parsed [name].toString ()); };
				add ("server");
				add ("photos_list");
				add ("aid");
				add ("hash");
				saveUrl.addQueryItem ("access_token", authKey);

				if (!info.Description_.isEmpty ())
					saveUrl.addQueryItem ("caption", info.Description_);

				RequestQueue_->Schedule ([this, saveUrl, info]
					{
						const auto req = Proxy_->GetNetworkAccessManager ()->get (QNetworkRequest (saveUrl));
						PhotoSave2Info_ [req] = info;
						connect (req,
								SIGNAL (finished ()),
								this,
								SLOT (handlePhotosSaved ()));
					}, this);
			});
	}

	void UploadManager::handlePhotosSaved ()
	{
		auto reply = qobject_cast<QNetworkReply*> (sender ());
		reply->deleteLater ();

		const auto& item = PhotoSave2Info_.take (reply);

		const auto& data = reply->readAll ();
		QDomDocument doc;
		if (!doc.setContent (data))
		{
			qWarning () << Q_FUNC_INFO
					<< "cannot parse reply"
					<< data;
			return;
		}

		QStringList ids;
		auto photoElem = doc
				.documentElement ()
				.firstChildElement ("photo");
		while (!photoElem.isNull ())
		{
			ids << QString ("%1_%2")
					.arg (photoElem.firstChildElement ("owner_id").text ())
					.arg (photoElem.firstChildElement ("pid").text ());
			photoElem = photoElem.nextSiblingElement ("photo");
		}

		Acc_->Schedule ([this, ids] (const QString& authKey) -> void
			{
				QUrl getUrl ("https://api.vk.com/method/photos.getById.xml");
				getUrl.addQueryItem ("photos", ids.join (","));
				getUrl.addQueryItem ("photo_sizes", "1");
				getUrl.addQueryItem ("access_token", authKey);
				RequestQueue_->Schedule ([this, getUrl]
					{
						connect (Proxy_->GetNetworkAccessManager ()->get (QNetworkRequest (getUrl)),
								SIGNAL (finished ()),
								Acc_,
								SLOT (handlePhotosInfosFetched ()));
					}, this);
			});

		// TODO remember to add url
		emit itemUploaded (item, {});
	}
}
}
}
