Revision 1 (by moose, 2006/03/06 10:00:33) Initial Import
#include <qfileinfo.h>
#include <qfile.h>
#include <qdir.h>
#include <qlibrary.h>
#include <dlfcn.h>

#include <string.h>
#include <errno.h>

#include "MadApp.h"
#include "MadMainWindow.h"
#include "MadPlugin.h"
//#include "MadChildFrame.h"
#include "MadConf.h"
#include <qmessagebox.h>

MadApp *app = 0;

MadApp::MadApp(int & argc, char ** argv) : QApplication(argc, argv), mw(main_window), conf(_conf), ml(media_library)
{
	//setStyle("cde");
	
	app = this;
	
	_conf = new MadConf("test.db");
	if(!_conf)
		mad_throw(MadException, "Failed to load config");
	
	main_window = new MadMainWindow();

	setMainWidget(main_window);

	main_window->show();

	LoadPlugins();
}

MadDBConnection *MadApp::getDB(const char *name)
{
	if(dbmap.contains(name)) {
		return dbmap[name]->getDB();
	}
	
	return 0;
}

bool MadApp::registerDBHandler(const char *name, MadDBHandler *hndl)
{
	if(!dbmap.contains(name)) {
		dbmap[name] = hndl;
		return true;
	}
	
	return false;
}

bool MadApp::unregisterDBHandler(const char *name)
{
	if(dbmap.contains(name)) {
		dbmap.erase(name);
		return true;
	}
	
	return false;
}

/*
Some ideas:
===========
 o instead of a QPtrList for the plugin list, use a hash and grap the name for the key.
 o seperate LoadPlugins into LoadPlugin and LoadPlugins
 o add InstallPlugin UninstallPlugin, UnloadPlugin, RehashPlugin/CyclePlugin (deinit/init cycle)

*/

void MadApp::rescanPlugins()
{

}

bool MadApp::LoadPlugin(QFileInfo fi)
{
	MadPlugin *plug = 0;
	QString name;
	
	if(fi.extension() != QString("so") && fi.extension() != QString("dll")) {
		printf("Skipping file: %s\n", fi.filePath().ascii());
		return true;
	}

	if(plugins.contains(fi.baseName())) {
		delete plug;
		return true;
	}
	
	printf( "Loading Plugin: %s\n", fi.filePath().ascii() );
	try {
		plug = new MadPlugin(fi.filePath().latin1());
		name = plug->GetName();
	} catch (const char *thing) {
		QMessageBox::warning(app->mw, "Error", QString().sprintf("%s: %s", thing, fi.baseName().ascii()));
		printf("Plug: %s\n", thing);
		printf("err: %s\n", strerror(errno));
		return false;
	}

	

	plugins[name] = plug;

	return true;
}

bool MadApp::installPlugin(const char *path)
{
	MadConfKey pkey;
	MadConfKey mkey;
	MadConfKey modkey;
	MadConfKey pathkey, loadkey;
	MadConfKeyValue pathval, loadval;
	MadPluginInstance *inst = 0;
	
	if(!conf->key(pkey, "plugins")) {
		printf("!plugins\n");
		return false;
	}
	
	
	if(!pkey.child(mkey, "modules"))
		return false;
	
	QFileInfo fi(path);
	
	if(mkey.child(modkey, fi.baseName())) {
		MadConfKey pathkey;
		if(modkey.child(pathkey, "path")) {
			MadConfKeyValue val;
			pathkey.value(val);
			
			if(fi.absFilePath() == val.getText())
				goto fail;
		}
	}
	
	if(!mkey.newChild(modkey, fi.baseName()))
		goto fail;
	
	if(!modkey.newChild(pathkey, "path"))
		goto fail;
	
	if(!pathkey.addValue(pathval, fi.absFilePath()))
		goto fail;
	
	if(!modkey.newChild(loadkey, "load"))
		goto fail;
	
	if(!loadkey.addValue(loadval, "1"))
		goto fail;
	
	if(!LoadPlugin(fi))
		goto fail;
	
	inst = plugins[fi.baseName()]->GetInstance();
	if(!inst)
		goto fail;
		
	inst->init();
		
	instances.append(inst);
	
	return true;
	
fail:
	// message here?
	return false;
}

bool MadApp::uninstallPlugin(const char *name)
{
	MadConfKey pkey, mkey, modkey;
	
	if(!conf->key(pkey, "plugins"))
		return false;
	
	if(!pkey.child(mkey, "modules"))
		return false;
	
	if(!mkey.child(modkey, name))
		return false;
	
	if(!modkey.erase())
		return false;
	
	return true;
}

bool MadApp::disablePlugin(const char *name)
{
	if(!plugins.contains(name))
		return false;

	MadPlugin *plug = plugins[name];	
	MadPluginInstance *inst = 0;
	for ( inst = instances.first(); inst; inst = instances.next() )
		if(inst->plug == plug) {
			instances.remove();
			delete inst;
		}
		
	delete plug;
	
	plugins.erase(name);

	printf("removed: %s\n", name);
		
	return true;
}

bool MadApp::enablePlugin(const char *name)
{	
	MadConfKey pkey;
	if(!conf->key(pkey, "plugins")) {
		printf("!plugins\n");
		return false;
	}
	
	MadConfKey mkey;
	if(!pkey.child(mkey, "modules"))
		return false;
	
	printf("modules\n");
	
	MadConfKey modkey;
	if(!mkey.child(modkey, name))
		return false;

	printf("modkey: %s\n", name);
	
	MadConfKey path;
	if(!modkey.child(path, "path"))
		return false;
	
	printf("path\n");
	
	MadConfKey load;
	if(!modkey.child(load, "load"))
		return false;
	
	printf("load\n");
	
	MadConfKeyValue lval;
	if(!load.value(lval))
		return false;
	
	printf("lval\n");
	
	if(!lval.set(true))
		return false;
	
	printf("lval.set\n");
	
	MadConfKeyValue pval;
	path.value(pval);
	QFileInfo fi(pval.getText());
	
	printf("pval\n");
	
	if(!LoadPlugin(fi))
		return false;
	
	MadPluginInstance *inst = plugins[name]->GetInstance();
	if(!inst)
		return false;
		
	inst->init();
		
	instances.append(inst);
	
	printf("added: %s\n", name);
	
	inst->start();
	
	return true;
}

int MadApp::pluginId(const char *name)
{
	MadConfKey pkey;
	if(!conf->key(pkey, "plugins")) {
		printf("!plugins\n");
		return -1;
	}
	
	MadConfKey mkey;
	if(!pkey.child(mkey, "modules"))
		return -1;

	MadConfKey modkey;
	if(!mkey.child(modkey, name))
		return -1;
	
	return modkey.getId();
}

bool MadApp::LoadPlugins()
{
	MadConfKeyValue dval;	
	MadPluginRMap rmap;

	char *dir = NULL;
	
	MadConfKey pkey;
	if(!conf->key(pkey, "plugins")) {
		printf("!plugins\n");
		return false;
	}
	
	MadConfKey mkey;
	if(!pkey.child(mkey, "modules"))
		return false;

	mkey.beginChild();
	
	MadConfKey modkey;
	while(mkey.step(modkey)) {
		MadConfKeyValue pval, lval;
		
		MadConfKey path;
		if(!modkey.child(path, "path"))
			continue;

		MadConfKey load;
	
		if(!modkey.child(load, "load"))
			continue;
		
		if(!load.value(lval))
			continue;
		
		if(!lval.getBool())
			continue;
		
		path.value(pval);
		QFileInfo fi(pval.getText());
		
		LoadPlugin(fi);
	}

	
	MadPluginMap::Iterator it;
	for ( it = plugins.begin(); it != plugins.end(); ++it ) {
		MadPlugin *plug = it.data();
		QStringList deps;
		if(!plug->Dependencies(deps)) {
			// rmap["__none__"] += plug->GetName();
			continue;
		}
		
		if(deps.count() < 1) {
			rmap["__none__"] += plug->GetName();
			continue;
		}
		for(QStringList::Iterator slit = deps.begin(); slit != deps.end(); ++slit) {
			rmap[*slit] += plug->GetName();
		}
	}
	// init plugins with no deps.
	for(QStringList::Iterator slit = rmap["__none__"].begin(); slit != rmap["__none__"].end(); ++slit) {
		
		MadPluginInstance *inst = plugins[*slit]->GetInstance();
		if(!inst)
			continue;
		
		inst->init();
		
		instances.append(inst);
	}

	rmap.erase("__none__");

	for (MadPluginRMap::Iterator rit = rmap.begin(); rit != rmap.end(); ++rit ) {
		for(QStringList::Iterator slit = rit.data().begin(); slit != rit.data().end(); ++rit) {
			MadPluginInstance *inst = plugins[*slit]->GetInstance();
			if(!inst)
				continue;
			
			if(!inst->init()) {
				delete plugins[*slit];
				plugins.remove(*slit);
			}
		
			instances.append(inst);
		}
	}

	for (MadPluginInstanceList::Iterator iit = instances.begin(); iit != instances.end(); ++iit ) {
		(*iit)->start();
	}
	
	return true;
}

int main(int argc, char **argv)
{
	try {
		MadApp app(argc, argv);
		return app.exec();
	} catch (MadException &me) {
		printf("%s:%s:%i:%s: %s\n", me.who(), me.file(), me.line(), me.func(), me.what());
	} catch (std::exception &ex) {
		printf("error: %s\n", ex.what());
	} catch (const char *msg) {
		printf("err: '%s'\n", msg);
	}
}


#if 0
	//try {
	//QLibrary *plug = new QLibrary("/home/moose/.projects/mad/plugins/test.so");
	void *plug = dlopen("/home/moose/.projects/mad/plugins/conf.so", RTLD_NOW);
	if(plug == 0) {
		printf("failed to load plugin: %s\n", dlerror());
		return false;
	}

	//VersionFunc _VersionFunc = (VersionFunc) plug->resolve("MPA_Version");
	VersionFunc _VersionFunc = (VersionFunc) dlsym(plug, "MPA_Version");
	printf("vf: %p\n", _VersionFunc);
	if(_VersionFunc == 0) {
		printf("Module missing Version method.\n");
		return false;
	}

	//InstanceFunc _InstanceFunc = (InstanceFunc) plug->resolve("MPA_Instance");
	InstanceFunc _InstanceFunc = (InstanceFunc) dlsym(plug, "MPA_Instance");
	printf("if: %p\n", _InstanceFunc);
	if(_InstanceFunc == 0) {
		printf("Module missing Instance method.\n");
		return false;
	}
	if( _VersionFunc() != MPA_VERSION) {
		printf("Incorrect module version.\n");
		return false;
	}

	MadPluginInstance *Instance = (MadPluginInstance *)_InstanceFunc((MadApp *)qApp);
	if(!Instance)
		printf("Failed to get module instance.\n");
	//} catch (const char *thing) {
	//	printf("Plug: %s\n", thing);
	//}
#endif