#include "telegrambotapi.h"
//#include <QMessageBox>
#include <QSslError>
#include <QDebug>
#include <QStringList>
#include <QDateTime>
#include <QHttpMultiPart>
#include "json.h"


const char * TelegramBotAPI::send_methods[] = {
	"sendMessage",
	"sendPhoto",
	"sendDocument",
										 };
const char * TelegramBotAPI::param_filename = "__filename";

TelegramBotAPI::TelegramBotAPI(QObject *parent) : QObject(parent) {
	is_connected = false;
	wait_server = false;
	has_stop = false;
	qRegisterMetaType<TelegramBotAPI::Message>("TelegramBotAPI::Message");
	qnam = new QNetworkAccessManager();
//	connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(httpFinished(QNetworkReply*)));
	connect(qnam, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
}


 TelegramBotAPI::~TelegramBotAPI() {
	has_stop = true;
//	msleep(60);
}


void TelegramBotAPI::setBotToken(QString arg) {
	if (m_botToken == arg) {
		checkBot();
		return;
	}
	is_connected = false;
	m_botToken = arg;
	botUrl = "https://api.telegram.org/bot"+m_botToken+"/";
	checkBot();
}


bool TelegramBotAPI::sendMessage(uint chat_id, TelegramBotAPI::MessageType mtype, QByteArray data, QString filename) {
	if (!is_connected || chat_id == 0 || data.isEmpty()) return false;
	SendQuery sq;
	sq.params["chat_id"] = chat_id;
	sq.method = send_methods[mtype];
	bool ok = false;
	switch (mtype) {
		case Text:
			sq.params["text"] = QString::fromUtf8(data.data(), data.size());
			ok = true;
		break;
		case Photo:
			if (!filename.isEmpty()) {
				sq.params[param_filename] = filename;
				sq.params["photo"] = data;
				ok = true;
			}
		break;
		case Document:
			if (!filename.isEmpty()) {
				sq.params[param_filename] = filename;
				sq.params["document"] = data;
				ok = true;
			}
		break;
		default:
		break;
	}
	if (ok) {
		sendmsg_que.enqueue(sq);
		if (wait_server) {
			if (reply->url().toString().split("/").last() == "getUpdates") reply->abort();
		}
	}
	return ok;
}


//void TelegramBot::run() {
//	while (!has_stop) {
//		mutex.lock();
//		if (qnam == 0) {
//			qnam = new QNetworkAccessManager();
//			connect(qnam, SIGNAL(finished(QNetworkReply*)),
//					 this, SLOT(httpFinished(QNetworkReply*)));
//			connect(qnam, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
//		}
//		if (!wait_server) {
//			if (!is_connected) checkBot();
//			updateBot();
//		}
//		mutex.unlock();
//		msleep(50);
//	}
//}


void TelegramBotAPI::updateBot() {
	if (!is_connected) return;
	QVariantMap vm;
	vm["timeout"] = 50;
	vm["offset"] = last_update;
	sendRequest("getUpdates", vm);
}


bool TelegramBotAPI::fileDownload() {
	if (download_list.isEmpty()) return false;
	bool ok = false;
	int rm = -1;
	for(int i=0; i<download_list.size(); i++) {
		DownloadFile & df = download_list[i];
		qDebug() << df.id << df.url << df.filename;
		if (df.url.isEmpty()) {
			QVariantMap fm;
			fm["file_id"] = df.id;
			sendRequest("getFile", fm);
			return true;
		} else {
			if (!df.downloaded) {
				QNetworkRequest nr;
				nr.setUrl(df.url);
				reply = qnam->get(nr);
				connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
				return true;
			} else {
				File f;
				f.chat_id = df.chat_id;
				f.filename = df.filename;
				f.data = df.data;
				emit newFile(f);
				rm = i;
				break;
			}
		}
	}
	if (rm >= 0) download_list.removeAt(rm);
	return ok;
}


void TelegramBotAPI::checkBot() {
	sendRequest("getMe", QVariantMap());
}


bool TelegramBotAPI::parseHeader(QString json, QVariant & res) {
	bool ok;
	QVariantMap map = QtJson::parse(json, ok).toMap();
	if (!ok) {
		emit errorOccured("parse JSON error");
		return false;
	}
	if (!map.value("ok").toBool()) {
		emit errorOccured("query error");
		return false;
	}
	res = map.value("result");
	return !res.isNull() && res.isValid();
}


void TelegramBotAPI::parseCheckBot(QString json) {
	QVariant res;
	if (!parseHeader(json, res)) {
		is_connected = false;
		emit botFail();
		return;
	}
	QVariantMap resm = res.toMap();
	m_botID = resm.value("id").toUInt();
	m_botName = resm.value("username").toString();
	is_connected = true;
	last_update = 0;
	emit botOK();
}


void TelegramBotAPI::parseFile(QString json) {
	QVariant res;
	if (!parseHeader(json, res)) {
		emit errorOccured("can't get file");
		return;
	}
	QVariantMap resm = res.toMap();
	QString fpath = resm["file_path"].toString();
	QString fid = resm["file_id"].toString();
	if (fpath.isEmpty()) return;
	QUrl furl = QUrl("https://api.telegram.org/file/bot"+m_botToken+"/"+fpath);
	qDebug() << "new file :" << furl;
	for (int i=0; i<download_list.size(); i++) {
		if (download_list[i].id == fid) {
			download_list[i].url = furl;
			if (download_list[i].filename.isEmpty())
				download_list[i].filename = fpath.split("/").last();
		}
	}
}


void TelegramBotAPI::parseMessages(QString json) {
	QVariant res;
	if (!parseHeader(json, res)) {
		emit errorOccured("can't update");
		return;
	}
	QVariantList resm = res.toList();
	foreach (QVariant v, resm) {
		QVariantMap mm = v.toMap();
		last_update = mm.value("update_id").toInt();
		Message m = parseMessage(mm.value("message"));
		if (m.id > 0 && !m.text.isEmpty()) {
			emit newMessage(m);
			emit newMessage(m.chat_id, m.text);
		}
	}
	last_update++;
	//qDebug() << "update_id" << last_update;
}


void TelegramBotAPI::startDownloadFile(uint chat_id, QVariantMap file) {
	if (!file.isEmpty()) {
		DownloadFile df;
		df.downloaded = false;
		df.id = file["file_id"].toString();
		df.chat_id = chat_id;
		df.filename = file["file_name"].toString();
		qDebug() << "new incomming file" << df.id;
		download_list << df;
	}
}


TelegramBotAPI::Message TelegramBotAPI::parseMessage(QVariant msg) {
	Message ret;
	QVariantMap map = msg.toMap();
	ret.id = map.value("message_id", 0).toUInt();
	QVariantMap chat = map.value("chat").toMap();
	ret.chat_id = chat.value("id", 0).toUInt();
	QVariantMap user = map.value("from").toMap();
	ret.user_id = user.value("id", 0).toUInt();
	ret.user_name = user.value("first_name").toString();
	ret.text = map.value("text").toString();
	ret.time = QDateTime::fromTime_t(map.value("date").toUInt());
	QVariantMap file;
	QVariantList photo = map.value("photo").toList();
	if (!photo.isEmpty()) {
		startDownloadFile(ret.chat_id, photo.last().toMap());
	}
	QVariantMap docum = map.value("document").toMap();
	startDownloadFile(ret.chat_id, docum);
	return ret;
}


void TelegramBotAPI::sslErrors(QNetworkReply*,const QList<QSslError> &errors) {
	QString errorString;
	foreach (const QSslError &error, errors) {
		if (!errorString.isEmpty())
			errorString += ", ";
		errorString += error.errorString();
	}
	emit errorOccured(errorString);
//	if (QMessageBox::warning(0, QString("HTTP"), QString("One or more SSL errors has occurred: %1").arg(errorString),
//							 QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Ignore)
//		== QMessageBox::Ignore) {
//		reply->ignoreSslErrors();
//	}
}


void TelegramBotAPI::httpFinished() {
//	qDebug() << "reply finished";
//	mutex.lock();
	QByteArray ba = reply->readAll();
	QStringList quest_path = reply->url().toString().split("/");
//	qDebug() << "quest list =" << quest_path.size();
	QString quest = quest_path.last();
	reply->deleteLater();
	wait_server = false;
//	mutex.unlock();
	if (quest_path.size() == 5) {
		QString s = QString::fromUtf8(ba.data(), ba.size());
	//	qDebug() << quest << s;
	//	if (quest.indexOf("?") > 0)
	//		quest.resize(quest.indexOf('?'));
		QVariant tmp;
		if (quest == "getMe") parseCheckBot(s);
		if (quest == "getFile") parseFile(s);
		if (quest == "getUpdates") parseMessages(s);
		if (quest == "sendMessage") parseHeader(s, tmp);
		qDebug() << "parse done" << quest << s;
	} else {
		for (int i=0; i<download_list.size(); ++i) {
			if (download_list[i].url == reply->url()) {
				download_list[i].data = ba;
				download_list[i].downloaded = true;
				break;
			}
		}
	}
	if (is_connected) {
		if (!fileDownload()) {
			if (sendmsg_que.isEmpty()) updateBot();
			else {
				SendQuery sq = sendmsg_que.dequeue();
				sendRequest(sq.method, sq.params);
			}
		}
	} else checkBot();
}


//void TelegramBot::httpFinished(QNetworkReply *) {
//	qDebug() << "get finished";
//}


void TelegramBotAPI::sendRequest(QString method, QVariantMap params) {
	wait_server = true;
	qDebug() << "startRequest:" << method;
	QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

	QString filename = params.take(param_filename).toString();
	QMapIterator<QString, QVariant> it(params);
	while (it.hasNext()) {
		it.next();
		QHttpPart part;
		if (it.value().type() == QVariant::ByteArray) {
			part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; filename=\""+filename+"\"; name=\""+it.key()+"\""));
			qDebug() << "sending file" << double(it.value().toByteArray().size()) / (1024*1024) << "Mb";
			part.setBody(it.value().toByteArray());
		} else {
			part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\""+it.key()+"\""));
			part.setBody(it.value().toString().toUtf8());
		}
		multiPart->append(part);
	}
	if (params.isEmpty()) {
		QHttpPart part;
		part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
		multiPart->append(part);
	}
	QNetworkRequest nr;
	nr.setUrl(QUrl(botUrl + method));
	reply = qnam->post(nr, multiPart);
	multiPart->setParent(reply); // delete the multiPart with the reply
	connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
//	connect(reply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
//	connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDataReadProgress(qint64,qint64)));
}
