#include <stable.h>

#include "webapi.h"

using namespace jop;

WebApi::WebApi() {
	baseUrl_ = "";
	sessionId_ = "";
	connect(&manager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(request_finished(QNetworkReply*)));
}

QString WebApi::baseUrl() const {
	return baseUrl_;
}

void WebApi::execRequest(HttpMethod method, const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString& tag) {
	if (baseUrl() == "") {
		qCritical() << "Trying to execute request before base URL has been set";
		QJsonObject obj;
		obj["error"] = "Trying to execute request before base URL has been set";
		emit requestDone(obj, tag);
		return;
	}

	QueuedRequest r;
	r.method = method;
	r.path = path;
	r.query = query;
	r.data = data;
	r.tag = tag;
	r.buffer = NULL;
	r.reply = NULL;
	queuedRequests_ << r;

	processQueue();
}

void WebApi::post(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::POST, path, query, data, tag); }
void WebApi::get(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::GET, path, query, data, tag); }
void WebApi::put(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::PUT, path, query, data, tag); }
void WebApi::del(const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString &tag) { execRequest(HttpMethod::DEL, path, query, data, tag); }
void WebApi::patch(const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString &tag) { execRequest(HttpMethod::PATCH, path, query, data, tag); }

void WebApi::setSessionId(const QString &v) {
	sessionId_ = v;
}

void WebApi::abortAll() {
	for (int i = 0; i < inProgressRequests_.size(); i++) {
		QueuedRequest r = inProgressRequests_[i];
		if (r.reply) {
			r.reply->abort();
			// TODO: Delete r.reply?
		}
	}

	for (int i = 0; i < queuedRequests_.size(); i++) {
		QueuedRequest r = queuedRequests_[i];
		if (r.reply) {
			r.reply->abort();
			// TODO: Delete r.reply?
		}
	}
	queuedRequests_.size();
}

void WebApi::processQueue() {
	if (!queuedRequests_.size() || inProgressRequests_.size() >= 50) return;
	QueuedRequest r = queuedRequests_.takeFirst();

	QString url = baseUrl_ + "/" + r.path;
	QUrlQuery query = r.query;

	if (sessionId_ != "") {
		query.addQueryItem("session", sessionId_);
	}

	url += "?" + query.toString(QUrl::FullyEncoded);

	QNetworkRequest* request = new QNetworkRequest(url);
	request->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

	QNetworkReply* reply = NULL;

	if (r.method == jop::PATCH) {
		// TODO: Delete buffer when done
		QBuffer* buffer = new QBuffer();
		buffer->open(QBuffer::ReadWrite);
		buffer->write(r.data.toString(QUrl::FullyEncoded).toUtf8());
		buffer->seek(0);
		r.buffer = buffer;
		reply = manager_.sendCustomRequest(*request, "PATCH", buffer);
	}

	if (r.method == jop::GET) {
	   reply = manager_.get(*request);
	}

	if (r.method == jop::POST) {
		reply = manager_.post(*request, r.data.toString(QUrl::FullyEncoded).toUtf8());
	}

	if (r.method == jop::PUT) {
		reply = manager_.put(*request, r.data.toString(QUrl::FullyEncoded).toUtf8());
	}

	if (r.method == jop::DEL) {
		reply = manager_.deleteResource(*request);
	}

	if (!reply) {
		qWarning() << "WebApi::processQueue(): reply object was not created - invalid request method";
		return;
	}

	r.reply = reply;
	r.request = request;
	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(request_error(QNetworkReply::NetworkError)));

	QStringList cmd;
	cmd << "curl";
	if (r.method == jop::PUT) cmd << "-X" << "PUT";
	if (r.method == jop::PATCH) cmd << "-X" << "PATCH";
	if (r.method == jop::DEL) cmd << "-X" << "DELETE";

	if (r.method != jop::GET && r.method != jop::DEL) {
		cmd << "--data" << "'" + r.data.toString(QUrl::FullyEncoded) + "'";
	}
	cmd << "'" + url + "'";

	qDebug().noquote() << cmd.join(" ");

	inProgressRequests_.push_back(r);
}

void WebApi::request_finished(QNetworkReply *reply) {
	QByteArray responseBodyBA = reply->readAll();
	QJsonObject response;
	QJsonParseError err;
	QJsonDocument doc = QJsonDocument::fromJson(responseBodyBA, &err);
	if (err.error != QJsonParseError::NoError) {
		QString errorMessage = "Could not parse JSON: " + err.errorString() + "\n" + QString(responseBodyBA);
		qWarning().noquote() << errorMessage;
		response["error"] = errorMessage;
	} else {
		response = doc.object();
		if (response.contains("error") && !response["error"].isNull()) {
			qWarning().noquote() << "API error:" << QString(responseBodyBA);
		}
	}

	for (int i = 0; i < inProgressRequests_.size(); i++) {
		QueuedRequest r = inProgressRequests_[i];
		if (r.reply == reply) {
			inProgressRequests_.erase(inProgressRequests_.begin() + i);
			emit requestDone(response, r.tag);
			break;
		}
	}

	processQueue();
}

void WebApi::request_error(QNetworkReply::NetworkError e) {
	qWarning() << "Network error" << e;
}

void jop::WebApi::setBaseUrl(const QString &v) {
	baseUrl_ = v;
}
