/*  -*- c++ -*-
    Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "pluginloader_p.h"

#include <kconfiggroup.h>
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <klocalizedstring.h>
#include <kstandarddirs.h>
#include <KPluginLoader>

#include <QtCore/QDebug>

using namespace Akonadi;

PluginMetaData::PluginMetaData()
{
}

PluginMetaData::PluginMetaData(const QString &lib, const QString &name, const QString &comment, const QString &cname)
    : library(lib)
    , nameLabel(name)
    , descriptionLabel(comment)
    , className(cname)
    , loaded(false)
{
}

PluginLoader *PluginLoader::mSelf = 0;

PluginLoader::PluginLoader()
{
    scan();
}

PluginLoader::~PluginLoader()
{
    qDeleteAll(mPluginLoaders);
    mPluginLoaders.clear();
}

PluginLoader *PluginLoader::self()
{
    if (!mSelf) {
        mSelf = new PluginLoader();
    }

    return mSelf;
}

QStringList PluginLoader::names() const
{
    return mPluginInfos.keys();
}

QObject *PluginLoader::createForName(const QString &name)
{
    if (!mPluginInfos.contains(name)) {
        kWarning(5300) << "plugin name \"" << name << "\" is unknown to the plugin loader." << endl;
        return 0;
    }

    PluginMetaData &info = mPluginInfos[name];

    //First try to load it staticly
    foreach (QObject *plugin, QPluginLoader::staticInstances()) {
        if (QLatin1String(plugin->metaObject()->className()) == info.className) {
            info.loaded = true;
            return plugin;
            break;
        }
    }

    if (!info.loaded) {
        KPluginLoader *loader = new KPluginLoader(info.library);
        if (loader->fileName().isEmpty()) {
            kWarning(5300) << loader->errorString();
            delete loader;
            return 0;
        }

        mPluginLoaders.insert(name, loader);
        info.loaded = true;
    }

    QPluginLoader *loader = mPluginLoaders.value(name);
    Q_ASSERT(loader);

    QObject *object = loader->instance();
    if (!object) {
        kWarning(5300) << "unable to load plugin for plugin name \"" << name << "\"." << endl;
        kWarning(5300) << "Error was:\"" << loader->errorString() << "\"." << endl;
        return 0;
    }

    return object;
}

PluginMetaData PluginLoader::infoForName(const QString &name) const
{
    if (!mPluginInfos.contains(name)) {
        return PluginMetaData();
    }

    return mPluginInfos.value(name);
}

void PluginLoader::scan()
{
    const QStringList list = KGlobal::dirs()->findAllResources("data", QLatin1String("akonadi/plugins/serializer/*.desktop"),
                                                               KStandardDirs::Recursive | KStandardDirs::NoDuplicates);
    for (int i = 0; i < list.count(); ++i) {
        const QString entry = list.at(i);

        KConfig config(entry, KConfig::SimpleConfig);
        if (config.hasGroup("Misc") && config.hasGroup("Plugin")) {
            KConfigGroup group(&config, "Plugin");

            const QString type = group.readEntry("Type").toLower();
            if (type.isEmpty()) {
                kWarning(5300) << "missing or empty [Plugin]Type value in \"" << entry << "\" - skipping" << endl;
                continue;
            }

            // read Class entry as a list so that types like QPair<A,B> are
            // properly escaped and don't end up being split into QPair<A
            // and B>.
            const QStringList classes = group.readXdgListEntry("X-Akonadi-Class");
            if (classes.isEmpty()) {
                kWarning(5300) << "missing or empty [Plugin]X-Akonadi-Class value in \"" << entry << "\" - skipping" << endl;
                continue;
            }

            const QString library = group.readEntry("X-KDE-Library");
            if (library.isEmpty()) {
                kWarning(5300) << "missing or empty [Plugin]X-KDE-Library value in \"" << entry << "\" - skipping" << endl;
                continue;
            }

            KConfigGroup group2(&config, "Misc");

            QString name = group2.readEntry("Name");
            if (name.isEmpty()) {
                kWarning(5300) << "missing or empty [Misc]Name value in \"" << entry << "\" - inserting default name" << endl;
                name = i18n("Unnamed plugin");
            }

            QString comment = group2.readEntry("Comment");
            if (comment.isEmpty()) {
                kWarning(5300) << "missing or empty [Misc]Comment value in \"" << entry << "\" - inserting default name" << endl;
                comment = i18n("No description available");
            }

            QString cname      = group.readEntry("X-KDE-ClassName");
            if (cname.isEmpty()) {
                kWarning(5300) << "missing or empty X-KDE-ClassName value in \"" << entry << "\"" << endl;
            }

            const QStringList mimeTypes = type.split(QLatin1Char(','), QString::SkipEmptyParts);

            kDebug(5300) << "registering Desktop file" << entry << "for" << mimeTypes << '@' << classes;
            Q_FOREACH (const QString &mimeType, mimeTypes) {
                Q_FOREACH (const QString &classType, classes) {
                    mPluginInfos.insert(mimeType + QLatin1Char('@') + classType, PluginMetaData(library, name, comment, cname));
                }
            }

        } else {
            kWarning(5300) << "Desktop file \"" << entry << "\" doesn't seem to describe a plugin " << "(misses Misc and/or Plugin group)" << endl;
        }
    }
}
