// vim: set tabstop=4 shiftwidth=4 noexpandtab
/*
Gwenview - A simple image viewer for KDE
Copyright 2000-2004 Aur�lien G�teau
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/
// STL
#include <list>

// TQt
#include <tqdir.h>

// KDE
#include <kdebug.h>
#include <kdesktopfile.h>
#include <kglobal.h>
#include <kservice.h>
#include <kstandarddirs.h>
#include <kurl.h>

// Local
#include "externaltoolcontext.h"
#include "externaltoolmanager.h"
namespace Gwenview {

#undef ENABLE_LOG
#undef LOG
//#define ENABLE_LOG
#ifdef ENABLE_LOG
#define LOG(x) kdDebug() << k_funcinfo << x << endl
#else
#define LOG(x) ;
#endif

// Helper functions for createContextInternal
inline bool mimeTypeMatches(const TQString& candidate, const TQString& reference) {
	if (reference=="*") return true;

	if (reference.right(1)=="*") {
		return candidate.startsWith(reference.left(reference.length()-1));
	} else {
		return candidate==reference;
	}
}

inline bool isSubSetOf(const TQStringList& subSet, const TQStringList& set) {
	// Simple implementation, might need some optimization
	TQStringList::ConstIterator itSubSet=subSet.begin();
	TQStringList::ConstIterator itSetBegin=set.begin();
	TQStringList::ConstIterator itSetEnd=set.end();

	for (; itSubSet!=subSet.end(); ++itSubSet) {
		bool matchFound=false;
		TQStringList::ConstIterator itSet=itSetBegin;
		for (; itSet!=itSetEnd; ++itSet) {
			if (mimeTypeMatches(*itSubSet, *itSet)) {
				matchFound=true;
				break;
			}
		}
		if (!matchFound) {
			return false;
		}
	}
	return true;
}


struct ExternalToolManagerPrivate {
	TQDict<KDesktopFile> mDesktopFiles;
	TQPtrList<KService> mServices;
	TQString mUserToolDir;

	/**
	 * Helper function for createContextInternal
	 */
	static bool compareKServicePtrByName(const KService* s1, const KService* s2) {
		Q_ASSERT(s1);
		Q_ASSERT(s2);
		return s1->name() < s2->name();
	}

	ExternalToolContext* createContextInternal(
		TQObject* parent, const KURL::List& urls, const TQStringList& mimeTypes)
	{
		bool onlyOneURL=urls.size()==1;
		
		// Only add to selectionServices the services which can handle all the
		// different mime types present in the selection
		//
		// We use std::list instead of TQValueList because it's not possible to
		// pass a sort functor to qHeapSort
		std::list<KService*> selectionServices;
		TQPtrListIterator<KService> it(mServices);
		for (; it.current(); ++it) {
			KService* service=it.current();
			if (!onlyOneURL && !service->allowMultipleFiles()) {
				continue;
			}
			
			TQStringList serviceTypes=service->serviceTypes();
			if (isSubSetOf(mimeTypes, serviceTypes)) {
				selectionServices.push_back(service);
			}
		}
		selectionServices.sort(compareKServicePtrByName);
		
		return new ExternalToolContext(parent, selectionServices, urls);
	}

};


// Helper function for ctor
void loadDesktopFiles(TQDict<KDesktopFile>& dict, const TQString& dirString) {
	TQDir dir(dirString);
	TQStringList list=dir.entryList("*.desktop");
	TQStringList::ConstIterator it=list.begin();
	for (; it!=list.end(); ++it) {
		KDesktopFile* df=new KDesktopFile( dir.filePath(*it) );
		dict.insert(*it, df);
	}
}

inline TQString addSlash(const TQString& _str) {
	TQString str(_str);
	if (str.right(1)!="/") str.append('/');
	return str;
}

ExternalToolManager::ExternalToolManager() {
	d=new ExternalToolManagerPrivate;

	// Getting dirs
	d->mUserToolDir=KGlobal::dirs()->saveLocation("appdata", "tools");
	d->mUserToolDir=addSlash(d->mUserToolDir);
	Q_ASSERT(!d->mUserToolDir.isEmpty());
	LOG("d->mUserToolDir:" << d->mUserToolDir);
	
	TQStringList dirs=KGlobal::dirs()->findDirs("appdata", "tools");
	LOG("dirs:" << dirs.join(","));

	// Loading desktop files
	TQDict<KDesktopFile> systemDesktopFiles;
	TQStringList::ConstIterator it;
	for (it=dirs.begin(); it!=dirs.end(); ++it) {
		if (addSlash(*it)==d->mUserToolDir) {
			LOG("skipping " << *it);
			continue;
		}
		LOG("loading system desktop files from " << *it);
		loadDesktopFiles(systemDesktopFiles, *it);
	}
	TQDict<KDesktopFile> userDesktopFiles;
	loadDesktopFiles(userDesktopFiles, d->mUserToolDir);

	// Merge system and user desktop files into our KDesktopFile dictionary
	d->mDesktopFiles=systemDesktopFiles;
	d->mDesktopFiles.setAutoDelete(true);
	TQDictIterator<KDesktopFile> itDict(userDesktopFiles);
	
	for (; itDict.current(); ++itDict) {
		TQString name=itDict.currentKey();
		KDesktopFile* df=itDict.current();
		if (d->mDesktopFiles.find(name)) {
			d->mDesktopFiles.remove(name);
		}
		if (df->readBoolEntry("Hidden")) {
			delete df;
		} else {
			d->mDesktopFiles.insert(name, df);
		}
	}

	d->mServices.setAutoDelete(true);
	updateServices();
}


ExternalToolManager::~ExternalToolManager() {
	delete d;
}

	
ExternalToolManager* ExternalToolManager::instance() {
	static ExternalToolManager manager;
	return &manager;
}


void ExternalToolManager::updateServices() {
	d->mServices.clear();
	TQDictIterator<KDesktopFile> it(d->mDesktopFiles);
	for (; it.current(); ++it) {
		KDesktopFile* desktopFile=it.current();
		// If sync() is not called, KService does not read up to date content
		desktopFile->sync();
		KService* service=new KService(desktopFile);
		d->mServices.append(service);
	}
}


TQDict<KDesktopFile>& ExternalToolManager::desktopFiles() const {
	return d->mDesktopFiles;
}


void ExternalToolManager::hideDesktopFile(KDesktopFile* desktopFile) {
	TQFileInfo fi(desktopFile->fileName());
	TQString name=TQString("%1.desktop").arg( fi.baseName(true) );
	d->mDesktopFiles.take(name);
	
	if (desktopFile->isReadOnly()) {
		delete desktopFile;
		desktopFile=new KDesktopFile(d->mUserToolDir + "/" + name, false);
	}
	desktopFile->writeEntry("Hidden", true);
	desktopFile->sync();
	delete desktopFile;
}


KDesktopFile* ExternalToolManager::editSystemDesktopFile(const KDesktopFile* desktopFile) {
	Q_ASSERT(desktopFile);
	TQFileInfo fi(desktopFile->fileName());

	TQString name=fi.baseName(true);
	d->mDesktopFiles.remove(TQString("%1.desktop").arg(name));
	
	return createUserDesktopFile(name);
}


KDesktopFile* ExternalToolManager::createUserDesktopFile(const TQString& name) {
	Q_ASSERT(!name.isEmpty());
	KDesktopFile* desktopFile=new KDesktopFile(
		d->mUserToolDir + "/" + name + ".desktop", false);
	d->mDesktopFiles.insert(TQString("%1.desktop").arg(name), desktopFile);	

	return desktopFile;
}


ExternalToolContext* ExternalToolManager::createContext(
	TQObject* parent, const KFileItemList* items)
{
	KURL::List urls;
	TQStringList mimeTypes;

	// Create our URL list and a list of the different mime types present in
	// the selection
	TQPtrListIterator<KFileItem> it(*items);
	for (; it.current(); ++it) {
		urls.append(it.current()->url());
		TQString mimeType=it.current()->mimetype();
		if (!mimeTypes.contains(mimeType)) {
			mimeTypes.append(mimeType);
		}
	}

	return d->createContextInternal(parent, urls, mimeTypes);
}


ExternalToolContext* ExternalToolManager::createContext(
	TQObject* parent, const KURL& url)
{
	KURL::List urls;
	TQStringList mimeTypes;
	
	urls.append(url);
	TQString mimeType=KMimeType::findByURL(url, 0, url.isLocalFile(), true)->name();
	mimeTypes.append(mimeType);
	
	return d->createContextInternal(parent, urls, mimeTypes);
}


} // namespace