/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QDomDocument>
#include <QNetworkRequest>
#include <QTextCodec>
#include <QTextStream>
#include <QWidget>

#include <chrono>
#include <cstdlib>
#include <iostream>
#include <thread>

#include "prefsmanager.h"
#include "scpaths.h"
#include "api/api_application.h"
#include "scribuscore.h"
#include "upgradechecker.h"

UpgradeChecker::UpgradeChecker()
{
	m_stability = "unstablesvn";
	m_isSVN = ScribusAPI::isSVN();
	major = ScribusAPI::getVersionMajor();
	minor = ScribusAPI::getVersionMinor();
	m_patchLevel = ScribusAPI::getVersionPatch();
	m_versionSuffix = ScribusAPI::getVersionSuffix().toLower().remove("svn").toInt();
#if defined(Q_OS_MACOS)
	m_platform = "MacOSX";
#elif defined(Q_OS_WIN32)
	m_platform = "Win32";
#else
	m_platform = "X11";
#endif
}

void UpgradeChecker::fetch()
{
	QString filename("scribusversions.xml");
	m_tempFile = ScPaths::tempFileDir()+filename;

	m_fin = false;

	m_file = new QFile(m_tempFile);
	m_networkManager = new QNetworkAccessManager(this);
	if (m_networkManager != nullptr && m_file != nullptr)
	{
		outputText( tr("No data on your computer will be sent to an external location"));
		QApplication::processEvents();
		if (m_file->open(QIODevice::ReadWrite))
		{
			QString hostname("services.scribus.net");
			QString filepath("/" + filename);
			QUrl fileURL(QString("https://%1%2").arg(hostname, filepath));
			outputText("<b>" + tr("Attempting to get the Scribus version update file:") + "</b>");
			outputText(fileURL.toString());

			QNetworkRequest networkRequest(fileURL);
			m_networkReply = m_networkManager->get(networkRequest);
			connect(m_networkReply, SIGNAL(finished()), SLOT(downloadFinished()));
			connect(m_networkReply, SIGNAL(readyRead()), SLOT(downloadReadyRead()));

			int waitCount=0;
			while (!m_fin && waitCount<20)
			{
				std::this_thread::sleep_for(std::chrono::seconds(1));
				++waitCount;
				if (m_writeToConsole)
					std::cout << ". " << std::flush;
				outputText( ".", true );
				QApplication::processEvents();
			}
			if (m_writeToConsole)
				std::cout << std::endl;
			if (waitCount >= 20)
				outputText("<b>" + tr("Timed out when attempting to get update file.") + "</b>");
			m_file->close();
		}
		m_file->remove();
	}
	delete m_file;
	m_file = nullptr;
	outputText( tr("Finished") );
	m_networkReply->deleteLater();
	m_networkManager->deleteLater();
}

void UpgradeChecker::downloadFinished()
{
	if (m_networkReply->error())
		outputText(QString("Failed: %1").arg(qPrintable(m_networkReply->errorString())));
	else
	{
		m_file->reset();
		process();
		m_fin = true;
		show(m_networkReply->error() != QNetworkReply::NoError);
	}
}

void UpgradeChecker::downloadReadyRead()
{
	m_file->write(m_networkReply->readAll());
}

bool UpgradeChecker::process()
{
	if (!m_file)
		return false;
	QTextStream ts(m_file);
	ts.setCodec(QTextCodec::codecForName("UTF-8"));
	QString errorMsg;
	int eline;
	int ecol;
	QDomDocument doc("scribusversions");
	QString data(ts.readAll());
	if (!doc.setContent( data, &errorMsg, &eline, &ecol )) 
	{
		if (data.contains("404 not found", Qt::CaseInsensitive))
			outputText("<b>" + tr("File not found on server") + "</b>");
		else
			outputText("<b>" + tr("Could not open version file: %1\nError:%2 at line: %3, row: %4").arg(m_file->fileName(), errorMsg).arg(eline).arg(ecol) + "</b>");
		return false;
	}
	
	QDomElement docElem = doc.documentElement();
	for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling())
	{
		QDomElement e = n.toElement();
		if (e.isNull())
			continue;
		if (e.tagName() == "release")
		{
			if (!e.hasAttribute("stability") || !e.hasAttribute("platform") || !e.hasAttribute("version"))
				continue;
			if (e.attribute("platform") != m_platform)
				continue;

			bool newVersion = false;
			QString verA(e.attribute("version"));
			QString verAStripped = verA.toLower();
			bool verIsCVS = verAStripped.contains("cvs");
			if (verIsCVS)
				verAStripped.remove("cvs");
			uint verMajor = verAStripped.section('.', 0, 0).toInt();
			uint verMinor = verAStripped.section('.', 1, 1).toInt();
			uint verRevsion1 = verAStripped.section('.', 2, 2).toInt();
			uint verRevsion2 = verAStripped.section('.', 3, 3).toInt();
			//If we found a release when a user is running an old CVS version
			if (verMajor == major && verMinor == minor && verRevsion1 == m_patchLevel && verRevsion2 == m_versionSuffix && m_isSVN && !verIsCVS && !m_updates.contains(verA))
				newVersion = true;
			else if (!(verMajor == major && verMinor == minor && verRevsion1 == m_patchLevel && verRevsion2 == m_versionSuffix))
			{
				//If we found a version that is not the same as what we are running
				if (((verMajor > major) ||
					(verMajor == major && verMinor > minor) ||
					(verMajor == major && verMinor == minor && verRevsion1 > m_patchLevel) ||
					(verMajor == major && verMinor == minor && verRevsion1 == m_patchLevel && verRevsion2 > m_versionSuffix))
					&& !m_updates.contains(verA))
				{
					newVersion = true;
				}
			}
			if (newVersion)
			{
				QString ver(verA);
				QString link(e.attribute("link", ""));
				if (!link.isEmpty())
				{
					QString linkStr = QString("<a href=\"%1\">%2</a>").arg(link, link);
					ver = QString("%1 : %2").arg(verA, linkStr);
				}
				m_updates.append(ver);
			}
		}
		else if (e.tagName() == "message")
		{
			m_message += e.text();
		}
	}
	return true;
}

QStringList UpgradeChecker::upgradeData() const
{
	return m_updates;
}

void UpgradeChecker::show(bool error)
{
	outputText("<br/>");
	if (error)
	{
		outputText("<b>" + tr("An error occurred while looking for updates for Scribus, please check your internet connection.") + "</b>");
		return;
	}
	if (m_updates.isEmpty())
		outputText("<b>" + tr("No updates are available for your version of Scribus %1").arg(ScribusAPI::getVersion()) + "</b>");
	else
	{
		outputText("<b>" + tr("One or more updates for your version of Scribus (%1) are available:").arg(ScribusAPI::getVersion()) + "</b>");
		outputText( tr("This list may contain development/unstable versions."));
		for ( QStringList::Iterator it = m_updates.begin(); it != m_updates.end(); ++it )
			outputText(*it);
		outputText("<b>" + tr("Please visit www.scribus.net for details.") + "</b>");
		outputText("<b>" + tr("If you have installed Scribus from a package management system, for example on a Linux-based operating system, your package manager may have this upgrade available.") + "</b>");
	}
	outputText(m_message);
}

void UpgradeChecker::outputText(const QString& text, bool /*noLineFeed*/)
{
	QString outText(text);
	outText.remove("<b>");
	outText.remove("</b>");
	outText.remove("<i>");
	outText.remove("</i>");
	outText.replace("<br>","\n");
	outText.replace("<br/>","\n");
	qDebug() << outText.toLocal8Bit().data();
}


void UpgradeChecker::reportError(const QString& s)
{
	if (!m_errorReported)
	{
		outputText("<br/><b>"+ tr("Error: %1").arg(s)+"</b>");
		m_errorReported = true;
	}
}

UpgradeCheckerGUI::UpgradeCheckerGUI(QTextBrowser *tb) : UpgradeChecker()
{
	m_outputWidget = tb;
	m_writeToConsole = false;
}

UpgradeCheckerGUI::~UpgradeCheckerGUI()
{
}

void UpgradeCheckerGUI::outputText(const QString& text, bool noLineFeed)
{
	QTextBrowser* w = m_outputWidget;
	if (!w)
		return;

	QString wText(w->toPlainText());
	wText.replace("\n","<br>");
	wText.remove("<qt>");
	wText.remove("</qt>");
	if (noLineFeed)
		w->setHtml("<qt>" + wText + text + "</qt>");
	else
		w->setHtml("<qt>" + wText + text + "<br>" + "</qt>");
}


