/*
 *   Copyright (C) 2008 Nick Shaforostoff <shaforostoff@kde.ru>
 *
 *   based on work by:
 *   Copyright (C) 2007 Thomas Georgiou <TAGeorgiou@gmail.com> and Jeff Cooper <weirdsox11@gmail.com>
 *
 *   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, see <http://www.gnu.org/licenses/>.
 */

#include "dictengine.h"
#include "dictplugin.h"
#include "config.h"

#include <QPluginLoader>
#include <QPointer>
#include <QMap>
#include <QTimer>
#include <QTime>
#include <QSettings>
#include <KDebug>
// #include <KLocale>

#include <Plasma/DataContainer>

#define MEMORYFREE_DELAY 60000


/**
 * This class represents a dictionary provided by a plugin
 */
class Dictionary
{
    public:
        Dictionary(const QString &plugin, const QString &name)
            : m_plugin(plugin)
            , m_name(name)
        {}
        Dictionary()
        {}

        const QString &plugin() const {return m_plugin;}
        const QString &name() const {return m_name;}
        void setPlugin(const QString &plugin) {m_plugin=plugin;}
        void setName(const QString &name) {m_name=name;}
        bool operator == (const Dictionary &dict) {return m_name==dict.m_name && m_plugin==dict.m_plugin;}

    private:
        QString m_plugin;
        QString m_name;
};


struct QStarDictEngine::Private
{
    QString currentWord;
    QTimer timer;

    QHash<QString, QPointer<QPluginLoader> > plugins; //name to pointer
//     QList<Dictionary> dicts;
//     QHash<QString, QString> dictToPlugin; //name to metastructure
    QHash<QString, Dictionary> dicts; //name to metastructure

};






QStarDictEngine::QStarDictEngine(QObject* parent, const QVariantList& args)
    : Plasma::DataEngine(parent, args)
    , d(new Private)
{
    Q_UNUSED(args)
//     QTime a;a.start();
#ifdef Q_OS_WIN
    QFileInfoList files = QDir(QSTARDICT_PLUGINS_DIR).entryInfoList(QStringList("*0.dll"),QDir::Files|QDir::NoDotAndDotDot);
    for (QFileInfoList::const_iterator i = files.begin(); i != files.end(); ++i)
        d->plugins[i->fileName().left(i->fileName().length()-5))]=0;
#else
    qWarning()<<QSTARDICT_PLUGINS_DIR;
    QFileInfoList files = QDir(QSTARDICT_PLUGINS_DIR).entryInfoList(QStringList("lib*.so"),QDir::Files|QDir::NoDotAndDotDot);
    for (QFileInfoList::const_iterator i = files.begin(); i != files.end(); ++i)
        d->plugins[i->fileName().mid(3, i->fileName().length()-6)]=0;
#endif



    // use cache to not load redudant plugins? 16->10 ms
//     QSettings settings("qstardict","qstardict");
//     m_dictDirs = settings.value("Multitran/dictDirs", m_dictDirs).toStringList();

    d->timer.setInterval(MEMORYFREE_DELAY);
    d->timer.setSingleShot(true);
    connect(&d->timer, SIGNAL(timeout()), this, SLOT(unloadPlugins()));


    QList<QString> plugins=d->plugins.keys();
    for (QList<QString>::const_iterator i = plugins.constBegin(); i != plugins.constEnd(); ++i)
    {
//         QStringList dicts = settings.value(*i+"/dicts", QStringList()).toStringList();
//         if (!dicts.isEmpty())
//         {
            QStarDict::DictPlugin* plugin = dictPlugin(*i);
            if (!plugin)
                continue;
            QStringList dicts=plugin->availableDicts();
//             settings.setValue(*i+"/dicts", QVariant(dicts));
//            plugin->setdicts(dicts);
//         }
        for (QStringList::const_iterator j = dicts.constBegin(); j != dicts.constEnd(); ++j)
            d->dicts[*j]=Dictionary(*i, *j);
//             d->dicts<<Dictionary(*i, *j);
    }
//     qWarning()<<a.elapsed();

}

QStarDictEngine::~QStarDictEngine()
{
    unloadPlugins();
    delete d;
}



QStarDict::DictPlugin* QStarDictEngine::dictPlugin(const QString &name)
{
    if (! d->plugins.contains(name) )
        return 0;
    if (! d->plugins.value(name) )
    {
#ifdef Q_OS_WIN
        QString pluginFilename = QSTARDICT_PLUGINS_DIR "/" + name + "0.dll";
#else 
        QString pluginFilename = QSTARDICT_PLUGINS_DIR "/" "lib" + name + ".so";
#endif
        QPluginLoader* plugin = new QPluginLoader(pluginFilename);
        if (! plugin->load())
        {
            kWarning() << plugin->errorString();
            delete plugin;
            return 0;
        }
        d->plugins[name]=plugin;
    }

    return qobject_cast<QStarDict::DictPlugin*>(d->plugins.value(name)->instance());
}

void QStarDictEngine::unloadPlugins()
{
    for (QHash<QString, QPointer<QPluginLoader> >::iterator i = d->plugins.begin(); i != d->plugins.end(); ++i)
    {
        if (*i)
        {
            (*i)->instance()->deleteLater();
            (*i)->deleteLater();
        }
    }

}

bool QStarDictEngine::sourceRequestEvent(const QString &word)
{
    qWarning()<<"sourceRequestEvent"<<word;
    d->timer.start();//delay freeing resources / activate delayed resources freeing

    if (word=="list-dictionaries")
    {
        QStringList result;
        int i=d->plugins.keys().size();
        while (--i>=0)
        {
            QStarDict::DictPlugin* plugin = dictPlugin(d->plugins.keys().at(i));
            if (!plugin)
                continue;
            result << plugin->availableDicts();
        }
        setData("list-dictionaries", "dictionaries", result);
        qWarning()<<result;
        return true;
    }

    QString simplifiedWord;
    QStringList queriedDicts;
    int pos=word.indexOf(':');
    if (pos!=-1)
    {
        queriedDicts=word.left(pos).split(',');
        simplifiedWord=word.mid(pos+1).simplified();
    }
    else
        simplifiedWord=word.simplified();
    qWarning()<<"simplifiedWord"<<simplifiedWord;
    qWarning()<<"queriedDicts"<<queriedDicts;

    if (queriedDicts.isEmpty())
        for (QHash<QString,Dictionary>::const_iterator i = d->dicts.constBegin(); i != d->dicts.constEnd(); ++i)
            queriedDicts<<i->name();

    d->currentWord = word;
    QString result;

    if (simplifiedWord.length() == 0)
    {
        setData(d->currentWord, "text", QString());
        return true;
    }

    for (QStringList::const_iterator i = queriedDicts.constBegin(); i != queriedDicts.constEnd(); ++i)
    {
        if (!d->dicts.contains(*i))
            continue;
        QStarDict::DictPlugin* plugin = dictPlugin(d->dicts.value(*i).plugin());
        if (!plugin)
            continue;
        QStringList ld=plugin->loadedDicts();
        if (!ld.contains(*i))
            plugin->setLoadedDicts(ld<<*i);
        if ( !plugin->isTranslatable(*i, simplifiedWord))
            continue;
        QStarDict::DictPlugin::Translation translation = plugin->translate(*i, simplifiedWord);
        if (translation.translation().isEmpty())
            continue;
        result += "<p>\n"
            "<font class=\"dict_name\">" + translation.dictName() + "</font><br>\n"
            "<font class=\"title\">" + translation.title() + "</font><br>\n"
            + translation.translation() + "</p>\n";
    }

    setData(d->currentWord, "text", result);
    return true;
}

K_EXPORT_PLASMA_DATAENGINE(qstardict, QStarDictEngine)
