#include "SH_plugins_web.h"
#include "SH_runtime.h"
#include <QTimer>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>


SHPluginsWeb::SHPluginsWeb() {
	setBaseURL("https://plugins.shs.tools");
	nam = new QNetworkAccessManager();
}


SHPluginsWeb::~SHPluginsWeb() {
	piDeleteSafety(nam);
}


void SHPluginsWeb::setBaseURL(const QString & url) {
	base_url = url;
	if (!base_url.endsWith('/')) base_url += "/";
	base_url += "v" + QString::number(SHS_VERSION_MAJOR) + "/";
}


void SHPluginsWeb::requestPluginsList() {
	list_done = false;
	manifests_list.clear();
	sendGet("plugins_list", [this](QNetworkReply * r){
		listReaded(r->readAll());
	});
}


bool SHPluginsWeb::waitPluginsList() {
	//qDebug() << "wait ...";
	PITimeMeasurer tm;
	while (!list_done) {
		piMinSleep();
		QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
		if (tm.elapsed_s() >= 30.)
			return false;
	}
	//qDebug() << "wait done";
	return true;
}


bool SHPluginsWeb::downloadPlugin(const SHPluginManifest & manifest, QString dir_to) {
	if (manifest.file.isEmpty()) return false;
	auto mpath = QDir(dir_to).absoluteFilePath(QFileInfo(manifest.manifestpath).fileName());
	auto lpath = QDir(dir_to).absoluteFilePath(manifest.file);
	QString lpath_tmp = lpath + ".tmp";
	bool done = false, error = false;
	sendGet(manifest.libraryDownloadPath(),
		[&done,&error,lpath,lpath_tmp,manifest,mpath](QNetworkReply * r){
			done = true;
			if (!writeFile(lpath_tmp, r->readAll())) {
				qDebug() << "[SHPluginsWeb::downloadPlugin] Error: can`t write" << lpath_tmp;
				error = true;
				return;
			}
			if (!writeFile(mpath, manifest.manifestcontent)) {
				qDebug() << "[SHPluginsWeb::downloadPlugin] Error: can`t write" << mpath;
				error = true;
				return;
			}
			QFile::remove(lpath);
			QFile::rename(lpath_tmp, lpath);
			qDebug() << "[SHPluginsWeb::downloadPlugin] Downloaded" << lpath;
		},
		[&done,&error](QNetworkReply * r){
			done = true;
			error = true;
		}
	);
	qDebug() << "wait ...";
	while (!done) {piMinSleep(); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);}
	qDebug() << "wait done";
	return !error;
	//qDebug() << mpath << lpath << manifest.libraryDownloadPath();
}


bool SHPluginsWeb::writeFile(const QString & path, const QByteArray & data) {
	QFile f(path);
	if (!f.open(QIODevice::ReadWrite))
		return false;
	f.resize(0);
	f.write(data);
	f.close();
	return true;
}


QNetworkReply * SHPluginsWeb::sendGet(const QString & path, std::function<void (QNetworkReply *)> on_success, std::function<void (QNetworkReply *)> on_error) {
	QNetworkRequest req(base_url + path);
	auto * rep = nam->get(req);
	if (!rep) return nullptr;
	connect(rep, &QNetworkReply::finished, this, [rep,on_success,on_error](){
		rep->deleteLater();
		if (rep->error() != QNetworkReply::NoError) {
			qDebug() << "[SHPluginsWeb::sendGet] Error:" << rep->errorString();
			if (on_error) on_error(rep);
			return;
		}
		if (on_success) on_success(rep);
	});
	return rep;
}


void SHPluginsWeb::listReaded(const QByteArray & ba) {
	auto list = QString::fromUtf8(ba).split('\n');
	list.removeAll("");
	list_to_get.clear();
	for (const auto & i: list)
		if (i.contains(SHS_CMAKE_OS, Qt::CaseInsensitive) && i.contains(SHS_CMAKE_ARCH, Qt::CaseInsensitive))
			list_to_get << i;
	list_to_get_src_size = list_to_get.size();
	if (list_to_get.isEmpty()) return;
	emit downloadProgress(0);
	emit downloadStarted();
	queueNextFromList();
}


void SHPluginsWeb::manifestReaded(const QString & path, const QByteArray & ba) {
	PIScopeExitCall exit_call([this]{queueNextFromList();});
	SHPluginManifest manifest;
	if (!manifest.read(ba)) return;
	if (!manifest.checkVersions()) return;
	manifest.manifestpath = path;
	manifests_list << manifest;
	//qDebug() << "got" << manifest.name << manifest.manifestpath << manifest.system << manifest.arch;
}


void SHPluginsWeb::requestNextFromList() {
	if (list_to_get.isEmpty()) {
		emit downloadFinished();
		list_done = true;
		return;
	}
	auto path = list_to_get.takeLast();
	emit downloadProgress(piRound(100. * (1. - (list_to_get.size() / static_cast<double>(list_to_get_src_size)))));
	sendGet(path,
		[this](QNetworkReply * r){
			manifestReaded(r->property("_path_").toString(), r->readAll());
		},
		[this](QNetworkReply * r){
			queueNextFromList();
		}
	)->setProperty("_path_", path);
}


void SHPluginsWeb::queueNextFromList() {
	QTimer::singleShot(0, [this](){requestNextFromList();});
}
