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

//BibleTime includes
#include "btinstallmgr.h"
#include "util/cpointers.h"
#include "cbtconfig.h"

//TQt includes
#include <tqfile.h>
#include <tqfileinfo.h>

//TDE includes
#include <tdeapplication.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <kprocess.h>
#include <tdeconfig.h>
#include <tdemessagebox.h>

//Sword includes
#include <filemgr.h>
#include <swconfig.h>
#include <swbuf.h>
#include <map>
#include <utility>

//Stl includes
#include <functional>

using namespace sword;

namespace BookshelfManager {

	const TQString BTInstallMgr::Tool::LocalConfig::swordConfigFilename() {
		//  Q_ASSERT( CPointers::backend()->sysconfig );
		return TQString::fromLatin1("%1/.sword/sword.conf").arg(getenv("HOME"));
	}

	TQStringList BTInstallMgr::Tool::RemoteConfig::sourceList( sword::InstallMgr* mgr ) {
		//  tqWarning("BTInstallMgr::Tool::sourceList( sword::InstallMgr* mgr )");
		Q_ASSERT(mgr);
		Q_ASSERT(mgr->installConf);

		TQStringList names;

		//add Sword remote sources
		for (InstallSourceMap::iterator it = mgr->sources.begin(); it != mgr->sources.end(); it++) {
			names << TQString::fromLocal8Bit(it->second->caption);
		}

		// Add local directory sources
		SWConfig config(Tool::RemoteConfig::configFilename().latin1());
		sword::SectionMap::iterator sourcesSection = config.Sections.find("Sources");
		if (sourcesSection != config.Sections.end()) {
			sword::ConfigEntMap::iterator sourceBegin = sourcesSection->second.lower_bound("DIRSource");
			sword::ConfigEntMap::iterator sourceEnd = sourcesSection->second.upper_bound("DIRSource");

			while (sourceBegin != sourceEnd) {
				InstallSource is("DIR", sourceBegin->second.c_str());
				names << TQString::fromLatin1(is.caption.c_str());

				sourceBegin++;
			}
		}

		return names;
	}

	TQStringList BTInstallMgr::Tool::LocalConfig::targetList() {
		TQStringList names = CPointers::backend()->swordDirList();
		return names;
	}

	void BTInstallMgr::Tool::LocalConfig::setTargetList( const TQStringList& targets ) {
		//saves a new Sworc config using the provided target list
		TQString filename = TDEGlobal::dirs()->saveLocation("data", "bibletime/") + "sword.conf"; //default is to assume the real location isn't writable
		bool directAccess = false;

		TQFileInfo i(LocalConfig::swordConfigFilename());
		TQFileInfo dirInfo(i.dirPath(true));

		if ( i.exists() && i.isWritable() ) { //we can write to the file ourself
			filename = LocalConfig::swordConfigFilename();
			directAccess = true;
		}
		else if ( !i.exists() && dirInfo.isWritable() ) { // if the file doesn't exist but th eparent is writable for us, create it
			filename = LocalConfig::swordConfigFilename();
			directAccess = true;
		}

		bool setDataPath = false;
		SWConfig conf(filename.local8Bit());
		conf.Sections.clear();

		for (TQStringList::const_iterator it = targets.begin(); it != targets.end(); ++it) {
			TQString t = *it;
			if (t.contains( TQString("%1/.sword").arg(getenv("HOME")) )) {
				//we don't want HOME/.sword in the config
				continue;
			}
			else {
				conf["Install"].insert( std::make_pair(!setDataPath ? SWBuf("DataPath") : SWBuf("AugmentPath"), t.local8Bit().data()) );

				setDataPath = true;
			}
		}
		conf.Save();

		if (!directAccess) { //use tdesu to move the file to the right place
			TDEProcess *proc = new TDEProcess;
			*proc << "tdesu";
			*proc << TQString::fromLatin1("-c") << TQString("mv %1 %2").arg(filename).arg(LocalConfig::swordConfigFilename());
			proc->start(TDEProcess::Block);
		}
	}

	sword::InstallSource BTInstallMgr::Tool::RemoteConfig::source( sword::InstallMgr* mgr, const TQString& name ) {
		Q_ASSERT(mgr);

		InstallSourceMap::iterator source = mgr->sources.find(name.latin1());
		if (source != mgr->sources.end()) {
			return *(source->second);
		}
		else { //not found in Sword, may be a local DIR source
			SWConfig config(Tool::RemoteConfig::configFilename().latin1());
			SectionMap::iterator sourcesSection = config.Sections.find("Sources");
			if (sourcesSection != config.Sections.end()) {
				ConfigEntMap::iterator sourceBegin =
					sourcesSection->second.lower_bound("DIRSource");
				ConfigEntMap::iterator sourceEnd =
					sourcesSection->second.upper_bound("DIRSource");

				while (sourceBegin != sourceEnd) {
					InstallSource is("DIR", sourceBegin->second.c_str());
					if (!strcmp(is.caption, name.latin1()) ) { //found local dir source
						return is;
					}

					sourceBegin++;//next source
				}
			}
		}

		InstallSource is("EMPTY");   //default return value
		is.caption = "unknown caption";
		is.source = "unknown source";
		is.directory = "unknown dir";

		return is;
	}

	const bool BTInstallMgr::Tool::RemoteConfig::isRemoteSource( sword::InstallSource* is ) {
		Q_ASSERT(is);
		if (is)
			return !strcmp(is->type, "FTP");
		else
			return false;
	}

	void BTInstallMgr::Tool::RemoteConfig::addSource( sword::InstallSource* is ) {
		if (!is) {
			return;
		}

		SWConfig config(Tool::RemoteConfig::configFilename().latin1());
		if (!strcmp(is->type, "FTP")) {
			//make sure the path doesn't have a trailing slash, sword doesn't like it
			if (is->directory[ is->directory.length()-1 ] == '/') {
				is->directory--; //make one char shorter
			}

			config["Sources"].insert( std::make_pair(SWBuf("FTPSource"), is->getConfEnt()) );
		}
		else if (!strcmp(is->type, "DIR")) {
			config["Sources"].insert( std::make_pair(SWBuf("DIRSource"), is->getConfEnt()) );
		}
		config.Save();
	}

	void BTInstallMgr::Tool::RemoteConfig::initConfig() {
		SWConfig config(Tool::RemoteConfig::configFilename().latin1());
		config["General"]["PassiveFTP"] = "true";
		config.Save();
	}

	const TQString BTInstallMgr::Tool::RemoteConfig::configPath() {
		const char *envhomedir  = getenv("HOME");
		TQString confPath = TQString(envhomedir ? envhomedir : ".");
		confPath.append("/.sword/InstallMgr");

		return confPath;
	}

	const TQString BTInstallMgr::Tool::RemoteConfig::configFilename() {
		return (configPath() + "/InstallMgr.conf");
	}

	void BTInstallMgr::Tool::RemoteConfig::removeSource( sword::InstallMgr* mgr, sword::InstallSource* is) {
		Q_ASSERT(mgr);
		Q_ASSERT(is);

		SWConfig config(Tool::RemoteConfig::configFilename().latin1());

		//this code can probably be shortened by using the stl remove_if functionality
		std::pair< ConfigEntMap::iterator, ConfigEntMap::iterator > range =
			isRemoteSource(is)
			? config["Sources"].equal_range("FTPSource")
			: config["Sources"].equal_range("DIRSource");

		ConfigEntMap::iterator it = range.first;
		while (it != range.second) {
			if (it->second == is->getConfEnt()) {
				//    tqWarning("found the source!");
				config["Sources"].erase(it);
				break;
			}

			++it;
		}

		config.Save();
	}

	void BTInstallMgr::Tool::RemoteConfig::resetRemoteSources() {
		SWConfig config(Tool::RemoteConfig::configFilename().latin1());
		config["Sources"].erase( //remove all FTP sources
			config["Sources"].lower_bound("FTPSource"),
			config["Sources"].upper_bound("FTPSource")
		);
		config.Save();
	}

	void BTInstallMgr::Tool::RemoteConfig::resetLocalSources() {
		SWConfig config(Tool::RemoteConfig::configFilename().latin1());
		config["Sources"].erase( //remove all FTP sources
			config["Sources"].lower_bound("DIRSource"),
			config["Sources"].upper_bound("DIRSource")
		);
		config.Save();
	}

	CSwordBackend* BTInstallMgr::Tool::backend( sword::InstallSource* const is) {
		Q_ASSERT(is);
		if (!is) {
			return 0;
		}

		CSwordBackend* ret = 0;
		if (RemoteConfig::isRemoteSource(is)) {
			//     tqWarning("## remote backend for %s", is->localShadow.c_str());
			ret = new CSwordBackend( TQString(is->localShadow.c_str()), false );
		}
		else {
			//    tqWarning("## local  backend for %s", TQString(is->directory.c_str()).latin1());
			ret = new CSwordBackend( TQString(is->directory.c_str()) );
		}

		Q_ASSERT(ret);
		if (ret) {
			ret->initModules();
		}

		return ret;
	}

	BTInstallMgr::BTInstallMgr() : InstallMgr(Tool::RemoteConfig::configPath().latin1(), this) { //use this class also as status reporter
		this->setFTPPassive(true);
	}

	bool BTInstallMgr::isUserDisclaimerConfirmed() const
	{
		TDEConfig* config = CBTConfig::getConfig();
		config->setGroup("Remote Resources");
		if (config->readNumEntry("Allowed", 0) == 1) {
			return true;
		}
		else {
			if (KMessageBox::questionYesNo(0, i18n("Do you wish to allow BibleTime to access the Internet?\nWARNING: Click DENY if you live in a persecuted country!"), i18n("Confirm Internet Access"), i18n("DENY"), i18n("Allow") ) == KMessageBox::No ) {
				config->setGroup("Remote Resources");
				config->writeEntry("Allowed", 1);
				config->sync();
				return true;
			}
			else {
				return false;
			}
		}
	}

	BTInstallMgr::~BTInstallMgr() {
		terminate(); //make sure to close the connection
	}

	void BTInstallMgr::statusUpdate(double dltotal, double dlnow) {

		if (dlnow > dltotal)
			dlnow = dltotal;

		int totalPercent = (int)((float)(dlnow + m_completedBytes) / (float)(m_totalBytes) * 100.0);

		if (totalPercent > 100) {
			totalPercent = 100;
		}
		else if (totalPercent < 0) {
			totalPercent = 0;
		}

		int filePercent  = (int)((float)(dlnow) / (float)(dltotal+1) * 100.0);
		if (filePercent > 100) {
			filePercent = 100;
		}
		else if (filePercent < 0) {
			filePercent = 0;
		}

		emit completed(totalPercent, filePercent);
	}

	void BTInstallMgr::preStatus(long totalBytes, long completedBytes, const char* /*message*/) {
		tqWarning("pre Status: %i / %i", (int)totalBytes, (int)completedBytes);
		emit downloadStarted( "unknown filename" );

		m_completedBytes = completedBytes;
		m_totalBytes = (totalBytes > 0) ? totalBytes : 1; //avoid division by zero
	}

//	FTPTransport *BTInstallMgr::createFTPTransport(const char *host, StatusReporter *statusReporter) {
//		return new TDEIO_FTPTransport(host, statusReporter);
//	}


}

#include "btinstallmgr.moc"
