/***************************************************************************
    begin                : Mon Jan 13 2003
    copyright            : (C) 2003 by Andrew Sutton
    email                : ansutton@kent.edu
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

// Qt includes
#include <qstring.h>

// KDE includes
#include <kdebug.h>
#include <klibloader.h>

// u2 includes
#include "pluginloader.h"
#include "plugin.h"

using namespace Umbrello;

// static data
PluginLoader *PluginLoader::_instance = NULL;

PluginLoader::PluginLoader() :
        _plugins(),
        _categories()
{
    // preseed the categories
    _categories["metamodel"] = PluginList();
    _categories["storage"] = PluginList();
    _categories["visual"] = PluginList();
}

PluginLoader::~PluginLoader()
{
}

PluginLoader *
PluginLoader::instance()
{
    if(!_instance) _instance = new PluginLoader;
    return _instance;
}

Plugin *
PluginLoader::loadPlugin(const QString &name)
{
    KLibrary *lib = NULL;
    KLibFactory *factory = NULL;
    Plugin *plugin = NULL;
    PluginMap::iterator it;
    bool success = true;

    // if the plugin has already been loaded, increment
    // its reference and return.
    if((it = _plugins.find(name)) != _plugins.end()) {
        plugin = it.data();
        plugin->ref();
        return plugin;
    }

    // use KLibLoader to get a reference to the library
    lib = KLibLoader::self()->library(name.latin1());
    if(!lib) {
        kdError() << "failed loading plugin library " << name << endl;
        success = false;
    }

    // get the factory from the library
    if(success) {
        factory = lib->factory();
        if(!factory) {
            kdError() << "failed to find factory for " << name << endl;
            success = false;
        }
    }

    // use the factory to create the plugin
    if(success) {
        plugin = dynamic_cast<Plugin *>(factory->create((QObject*)0, name.latin1()));
        if(!plugin) {
            kdError() << "failed to create a plugin object for " << name << endl;
            success = false;
        }
        else {
            // we have to register the plugin here, otherwise, we can get
            // recursive loads
            _plugins[name] = plugin;
            _categories[plugin->category()].append(plugin);
        }
    }

    // initialize the plugin
    if(success && plugin) {
        success = plugin->init();
        if(!success) {
            // on failure, delete the plugin. this should cause the
            // library to unload.
            kdError() << "failure initializing " << name << endl;
            _categories[plugin->category()].remove(plugin);
            _plugins.remove(name);
            delete plugin;
        }
    }

    // finally, finally connect to the destroyed signal and keep a
    // reference to it
    if(success) {
        plugin->ref();
        connect(plugin, SIGNAL(destroyed(QObject *)), SLOT(slotDestroyed(QObject *)));
    }

    return plugin;
}

Plugin *
PluginLoader::findPlugin(const QString &name)
{
    Plugin *ret = NULL;
    PluginMap::iterator it = _plugins.find(name);
    if(it != _plugins.end()) {
        ret = it.data();
    }
    return ret;
}

void
PluginLoader::unloadPlugin(const QString &name)
{
    KLibLoader::self()->unloadLibrary(name.latin1());
}

const PluginLoader::PluginMap &
PluginLoader::plugins() const
{
    return _plugins;
}

const PluginLoader::CategoryMap &
PluginLoader::categories() const
{
    return _categories;
}

void
PluginLoader::slotDestroyed(QObject *obj)
{
    Plugin *plugin = static_cast<Plugin *>(obj);

    // we can't just use the name because its already been destroyed
    // at this point. we have to iterate thru and find the reference
    // by hand.

    PluginMap::iterator end(_plugins.end());
    for(PluginMap::iterator i = _plugins.begin(); i != end; ++i) {
        Plugin *p = i.data();
        if(p == plugin) {
            kdDebug() << "unloading plugin " << i.key() << endl;

            // remove it from the mapping
            _plugins.remove(i);
            break;
        }
    }
}

#include "pluginloader.moc"
