/**********************************************************************
 * 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 "serverhistorymanager.h"
#include <QStandardItemModel>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QtDebug>
#include <qjson/parser.h>
#include <interfaces/azoth/ihaveserverhistory.h>
#include "vkaccount.h"
#include "vkentry.h"

namespace LeechCraft
{
namespace Azoth
{
namespace Murm
{
	namespace
	{
		const auto DlgChunkCount = 100;

		enum CustomHistRole
		{
			UserUid = ServerHistoryRole::ServerHistoryRoleMax,
			UserName
		};
	}

	ServerHistoryManager::ServerHistoryManager (VkAccount *acc)
	: QObject { acc }
	, Acc_ { acc }
	, ContactsModel_ { new QStandardItemModel { this } }
	{
		ContactsModel_->setHorizontalHeaderLabels ({ tr ("Contact") });
	}

	QAbstractItemModel* ServerHistoryManager::GetModel () const
	{
		return ContactsModel_;
	}

	void ServerHistoryManager::RequestHistory (const QModelIndex& index, int offset, int count)
	{
		if (count < 0)
		{
			count = -count;
			offset = std::max (offset - count, -1);
		}
		else
			++offset;

		if (count > 100)
			count = 100;

		const auto nam = Acc_->GetCoreProxy ()->GetNetworkAccessManager ();

		const auto uid = index.data (CustomHistRole::UserUid).toULongLong ();

		auto getter = [count, offset, nam, uid, index, this]
				(const QString& key, const VkConnection::UrlParams_t& params) -> QNetworkReply*
			{
				QUrl url ("https://api.vk.com/method/messages.getHistory");
				url.addQueryItem ("access_token", key);
				url.addQueryItem ("uid", QString::number (uid));
				url.addQueryItem ("count", QString::number (count));
				url.addQueryItem ("offset", QString::number (offset));
				VkConnection::AddParams (url, params);

				LastOffset_ = offset;

				auto reply = nam->get (QNetworkRequest (url));
				MsgRequestState_ [reply] = RequestState { index, offset };
				connect (reply,
						SIGNAL (finished ()),
						this,
						SLOT (handleGotHistory ()));
				return reply;
			};

		Acc_->GetConnection ()->QueueRequest (getter);
	}

	void ServerHistoryManager::Request (int offset)
	{
		const auto nam = Acc_->GetCoreProxy ()->GetNetworkAccessManager ();

		auto getter = [offset, nam, this]
				(const QString& key, const VkConnection::UrlParams_t& params) -> QNetworkReply*
			{
				QUrl url ("https://api.vk.com/method/messages.getDialogs");
				url.addQueryItem ("access_token", key);
				url.addQueryItem ("count", QString::number (DlgChunkCount));
				url.addQueryItem ("offset", QString::number (offset));
				VkConnection::AddParams (url, params);

				LastOffset_ = offset;

				auto reply = nam->get (QNetworkRequest (url));
				connect (reply,
						SIGNAL (finished ()),
						this,
						SLOT (handleGotMessagesList ()));
				return reply;
			};

		Acc_->GetConnection ()->QueueRequest (getter);
	}

	void ServerHistoryManager::refresh ()
	{
		if (IsRefreshing_)
			return;

		IsRefreshing_ = true;

		if (const auto rc = ContactsModel_->rowCount ())
			ContactsModel_->removeRows (0, rc);

		MsgCount_ = -1;
		Request (0);
	}

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

		const auto& reqContext = MsgRequestState_.take (reply);

		const auto& data = reply->readAll ();
		bool ok = true;
		const auto& varmap = QJson::Parser {}.parse (data).toMap ();
		if (!ok)
		{
			qWarning () << Q_FUNC_INFO
					<< "cannot parse reply"
					<< data;
			return;
		}

		SrvHistMessages_t messages;
		for (const auto& var : varmap ["response"].toList ())
		{
			const auto& map = var.toMap ();
			if (map.isEmpty ())
				continue;

			const auto dir = map ["out"].toInt () ?
					IMessage::Direction::Out :
					IMessage::Direction::In;

			const auto& username = dir == IMessage::Direction::In ?
					reqContext.Index_.data (CustomHistRole::UserName).toString () :
					Acc_->GetSelf ()->GetEntryName ();

			messages.append ({
					dir,
					{},
					username,
					map ["body"].toString (),
					QDateTime::fromTime_t (map ["date"].toULongLong ()),
					{}
				});
		}

		for (int i = 0; i < messages.size (); ++i)
			messages [i].ID_ = QByteArray::number (reqContext.Offset_ + i);

		std::reverse (messages.begin (), messages.end ());

		emit serverHistoryFetched (reqContext.Index_,
				QByteArray::number (reqContext.Offset_), messages);
	}

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

		IsRefreshing_ = false;

		const auto& data = reply->readAll ();
		bool ok = true;
		const auto& varmap = QJson::Parser {}.parse (data).toMap ();
		if (!ok)
		{
			qWarning () << Q_FUNC_INFO
					<< "cannot parse reply"
					<< data;
			return;
		}

		auto varlist = varmap ["response"].toList ();
		if (varlist.isEmpty ())
			return;

		if (MsgCount_ == -1)
		{
			bool ok = false;
			const auto count = varlist.first ().toInt (&ok);
			if (ok)
			{
				MsgCount_ = count;
				qDebug () << Q_FUNC_INFO
						<< "detected"
						<< count
						<< "dialogs";
			}
		}

		for (const auto& var : varlist)
		{
			const auto& varmap = var.toMap ();
			if (varmap.isEmpty ())
				continue;

			// TODO handle MUCs
			if (varmap.contains ("admin_id"))
				continue;

			const auto uid = varmap ["uid"].toULongLong ();
			const auto ts = varmap ["date"].toULongLong ();

			const auto entry = Acc_->GetEntry (uid);
			if (!entry)
			{
				qWarning () << Q_FUNC_INFO
						<< "no entry for"
						<< uid;
				continue;
			}

			auto item = new QStandardItem (entry->GetEntryName ());
			item->setEditable (false);
			item->setData (QDateTime::fromTime_t (ts), ServerHistoryRole::LastMessageDate);
			item->setData (QVariant::fromValue<QObject*> (Acc_->GetEntry (uid)),
					ServerHistoryRole::CLEntry);
			item->setData (uid, CustomHistRole::UserUid);
			item->setData (entry->GetEntryName (), CustomHistRole::UserName);
			ContactsModel_->appendRow (item);
		}


		if (LastOffset_ + DlgChunkCount < MsgCount_)
		{
			IsRefreshing_ = true;
			Request (LastOffset_ + DlgChunkCount);
		}
	}
}
}
}
