/*********
*
* This file is part of BibleTime's source code, http://www.bibletime.info/.
*
* Copyright 1999-2008 by the BibleTime developers.
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
*
**********/

#include "btinstallthread.h"

#include "frontend/bookshelfmanager/btinstallmgr.h"
#include "frontend/bookshelfmanager/instbackend.h"
#include "util/cpointers.h"
#include "backend/managers/cswordbackend.h"

#include <QApplication>
#include <QString>
#include <QThread>
#include <QDir>

#include <QDebug>

// sword
#include <filemgr.h>

BtInstallThread::BtInstallThread(QObject* parent, QString moduleName, QString sourceName, QString destinationName)
	: QThread(parent),
	done(false),
	m_module(moduleName),
	m_destination(destinationName),
	m_source(sourceName),
    m_cancelled(false)
{
	m_iMgr = new BtInstallMgr();
}


BtInstallThread::~BtInstallThread()
{
	delete m_iMgr;
}

void BtInstallThread::run()
{
	qDebug() << "****************************************\nBtInstallThread::run, mod:" << m_module << "\n************************************";


	emit preparingInstall(m_module, m_source);

    m_installSource.reset(new sword::InstallSource(instbackend::source(m_source)));
    m_backendForSource.reset(instbackend::backend(*m_installSource));

	//make sure target/mods.d and target/modules exist
	//TODO: move this to some common precondition
	QDir dir(m_destination);
	if (!dir.exists()) {
		dir.mkdir(m_destination);
		qDebug() << "made directory" << m_destination;
	}
	if (!dir.exists("modules")) {
		dir.mkdir("modules");
		qDebug() << "made directory" << m_destination << "/modules";
	}
	if (!dir.exists("mods.d")) {
		dir.mkdir("mods.d");
		qDebug() << "made directory" << m_destination << "/mods.d";
	}

	QObject::connect(m_iMgr, SIGNAL(percentCompleted(int, int)), this, SLOT(slotManagerStatusUpdated(int, int)), Qt::QueuedConnection);
	QObject::connect(m_iMgr, SIGNAL(downloadStarted()), this, SLOT(slotDownloadStarted()), Qt::QueuedConnection);

	//check whether it's an update. If yes, remove existing module first
	//TODO: silently removing without undo if the user cancels the update is WRONG!!!
	removeModule();

	// manager for the destination path
	sword::SWMgr lMgr( m_destination.toLatin1() );

    if (instbackend::isRemote(*m_installSource)) {
		qDebug() << "calling install";
        int status = m_iMgr->installModule(&lMgr, 0, m_module.toLatin1(), m_installSource.get());
		if (status != 0) {
			qWarning() << "Error with install: " << status << "module:" << m_module;
		}
		else {
			done = true;
			emit installCompleted(m_module, m_source, status);
		}
	}
	else { //local source
		emit statusUpdated(m_module, 0);
        int status = m_iMgr->installModule(&lMgr, m_installSource->directory.c_str(), m_module.toLatin1());
		if (status > 0) {
			qWarning() << "Error with install: " << status << "module:" << m_module;
		}
		else if (status == -1) {
			// it was terminated, do nothing
		}
		else {
			emit statusUpdated(m_module, 100);
			done = true;
			emit installCompleted(m_module, m_source, status);
		}
	}
}

void BtInstallThread::slotStopInstall()
{
	qDebug() << "*************************************\nBtInstallThread::slotStopInstall" << m_module << "\n********************************";
	if (!done) {
		done = true;
		qDebug() << "*********************************\nBtInstallThread::slotStopInstall, installing" << m_module << "was cancelled\n**************************************";
		m_iMgr->terminate();
		//this->terminate(); // It's dangerous to forcibly stop, but we will clean up the files
		qDebug() << "BtInstallThread::slotStopInstall 2";
		//qApp->processEvents();
		// wait to terminate for some secs. We rather let the execution go on and cleaning up to fail than the app to freeze
		int notRun = this->wait(200);
		if (notRun) {
			this->terminate();
			this->wait(2);
			qDebug() << "installthread ("<< m_module << ") terminated, delete m_iMgr";
			delete m_iMgr; // this makes sure the ftp library will be cleaned up in the destroyer
			m_iMgr = 0;
		}
		qDebug() << "BtInstallThread::slotStopInstall 3";
		// cleanup: remove the module, remove the temp files
		if (true) {
			qDebug() << "BtInstallThread::slotStopInstall 4";
			// remove the installed module, just to be sure because mgr may
			// have been terminated when copying files
			removeModule();
			removeTempFiles();
			qDebug() << "BtInstallThread::slotStopInstall will emit installStopped...";
			emit installStopped(m_module, m_source);
		}
	}
}

void BtInstallThread::slotManagerStatusUpdated(int totalProgress, int /*fileProgress*/)
{
	//qDebug("BtInstallThread::slotManagerStatusUpdated");
	emit statusUpdated(m_module, totalProgress);
}

void BtInstallThread::slotDownloadStarted()
{
	qDebug("BtInstallThread::slotDownloadStarted");
	emit downloadStarted(m_module);
}

void BtInstallThread::removeModule()
{
	qDebug() << "BtInstallThread::removeModule start";
	CSwordModuleInfo* m;
	m = CPointers::backend()->findModuleByName(m_module);
	if (!m) {
		m = instbackend::backend(instbackend::source(m_destination.toLatin1()))->findModuleByName(m_module);
	}
	if (m) { //module found?
		qDebug() << "BtInstallThread::removeModule, module" << m_module << "found";
		QString prefixPath = m->config(CSwordModuleInfo::AbsoluteDataPath) + "/";
		QString dataPath = m->config(CSwordModuleInfo::DataPath);
		if (dataPath.left(2) == "./") {
			dataPath = dataPath.mid(2);
		}

		if (prefixPath.contains(dataPath)) {
			prefixPath.remove( prefixPath.indexOf(dataPath), dataPath.length() );
		}
		else {
			prefixPath = QString::fromLatin1(CPointers::backend()->prefixPath);
		}

		sword::SWMgr mgr(prefixPath.toLatin1());
		BtInstallMgr iMgr;
		iMgr.removeModule(&mgr, m->name().toLatin1());
	} else {
		qDebug() << "BtInstallThread::removeModule, module" << m_module << "not found";
	}
}

void BtInstallThread::removeTempFiles()
{
	qDebug("BtInstallThread::removeTempFiles start");

	// (take the remote conf file for this module, take DataPath,
	// take the absolute path of the InstallMgr)

	//sword::InstallSource is = instbackend::source(m_source);
    if (instbackend::isRemote(*m_installSource)) {
		// get the path for the module temp files
		CSwordModuleInfo* mInfo = m_backendForSource->findModuleByName(m_module);
		QString dataPath = mInfo->config(CSwordModuleInfo::AbsoluteDataPath);
		qDebug() << "Delete path:" << dataPath;
		// it's easier to use sword than qt
		sword::FileMgr::removeDir(dataPath.toLatin1().data());
	}
}
