/*
   This file is part of the Nepomuk KDE project.
   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) version 3, or any
   later version accepted by the membership of KDE e.V. (or its
   successor approved by the membership of KDE e.V.), which shall
   act as a proxy defined in Section 6 of version 3 of the license.

   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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library.  If not, see <http://www.gnu.org/licenses/>.

   The basis for this file was generated by qdbusxml2cpp version 0.7
   Command line was: qdbusxml2cpp -a datamanagementadaptor -c DataManagementAdaptor -m org.kde.nepomuk.DataManagement.xml

   qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 */

#include "datamanagementadaptor.h"
#include "datamanagementmodel.h"
#include "datamanagementcommand.h"
#include "simpleresourcegraph.h"
#include "dbustypes.h"

#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtCore/QThreadPool>
#include <QtDBus/QDBusMetaType>


Nepomuk2::DataManagementAdaptor::DataManagementAdaptor(Nepomuk2::DataManagementModel *parent)
    : QObject(parent),
      m_model(parent),
      m_namespacePrefixRx(QLatin1String("(\\w+)\\:(\\w+)"))
{
    DBus::registerDBusTypes();

    // FIXME: it is not sufficient to put this register call in DBus::registerDBusTypes()
    //        might have to do with the latter being part of libnepomukcore.
    qDBusRegisterMetaType<__nepomuk_QHashQStringQString>();

    m_threadPool = new QThreadPool(this);

    // N threads means N connections to Virtuoso
    m_threadPool->setMaxThreadCount(10);

    // This is hack to slow down the indexing. A lot of processes seem to be indexing things
    // at the same time, and this leads of very high cpu usage along with occasional transaction
    // failures. As a temporary measure, it would be best to only allow one storeResource at a
    // time for 4.11.1
    m_storeResourcesThreadPool = new QThreadPool(this);
    m_storeResourcesThreadPool->setMaxThreadCount(1);
}

Nepomuk2::DataManagementAdaptor::~DataManagementAdaptor()
{
    // make sure all commands are done before letting deletion continue
    m_threadPool->waitForDone();
}

void Nepomuk2::DataManagementAdaptor::addProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new AddPropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk2::DBus::resolveDBusArguments(values), app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::addProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
{
    addProperty(QStringList() << resource, property, QVariantList() << Nepomuk2::DBus::resolveDBusArguments(value.variant()), app);
}

QString Nepomuk2::DataManagementAdaptor::createResource(const QStringList &types, const QString &label, const QString &description, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new CreateResourceCommand(decodeUris(types), label, description, app, m_model, message()));
    // QtDBus will ignore this return value
    return QString();
}

QString Nepomuk2::DataManagementAdaptor::createResource(const QString &type, const QString &label, const QString &description, const QString &app)
{
    return createResource(QStringList() << type, label, description, app);
}

QList<Nepomuk2::SimpleResource> Nepomuk2::DataManagementAdaptor::describeResources(const QStringList &resources, int flags, const QStringList& targetParties)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new DescribeResourcesCommand(decodeUris(resources), Nepomuk2::DescribeResourcesFlags(flags), decodeUris(targetParties), m_model, message()));
    // QtDBus will ignore this return value
    return QList<SimpleResource>();
}

QHash< QString, QString > Nepomuk2::DataManagementAdaptor::storeResources(const QList< Nepomuk2::SimpleResource >& resources, int identificationMode, int flags, const Nepomuk2::PropertyHash& additionalMetadata, const QString& app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    StoreResourcesCommand* command = new StoreResourcesCommand(resources, app, identificationMode, flags, additionalMetadata, m_model, message());
    m_storeResourcesThreadPool->start(command);
    // QtDBus will ignore this return value
    return QHash<QString, QString>();
}

void Nepomuk2::DataManagementAdaptor::mergeResources(const QString &resource1, const QString &resource2, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    QList<QUrl> resources;
    resources << decodeUri(resource1) << decodeUri(resource2);
    enqueueCommand(new MergeResourcesCommand(resources, app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::mergeResources(const QStringList& resources, const QString& app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new MergeResourcesCommand(decodeUris(resources), app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeDataByApplication(int flags, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new RemoveDataByApplicationCommand(app, flags, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeDataByApplication(const QStringList &resources, int flags, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new RemoveResourcesByApplicationCommand(decodeUris(resources), app, flags, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeProperties(const QStringList &resources, const QStringList &properties, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new RemovePropertiesCommand(decodeUris(resources), decodeUris(properties), app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeProperties(const QString &resource, const QString &property, const QString &app)
{
    removeProperties(QStringList() << resource, QStringList() << property, app);
}

void Nepomuk2::DataManagementAdaptor::removeProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new RemovePropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk2::DBus::resolveDBusArguments(values), app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
{
    removeProperty(QStringList() << resource, property, QVariantList() << Nepomuk2::DBus::resolveDBusArguments(value.variant()), app);
}

void Nepomuk2::DataManagementAdaptor::removeResources(const QStringList &resources, int flags, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new RemoveResourcesCommand(decodeUris(resources), app, flags, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::removeResources(const QString &resource, int flags, const QString &app)
{
    removeResources(QStringList() << resource, flags, app);
}

void Nepomuk2::DataManagementAdaptor::setProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new SetPropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk2::DBus::resolveDBusArguments(values), app, m_model, message()));
}

void Nepomuk2::DataManagementAdaptor::setProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
{
    setProperty(QStringList() << resource, property, QVariantList() << Nepomuk2::DBus::resolveDBusArguments(value.variant()), app);
}

void Nepomuk2::DataManagementAdaptor::enqueueCommand(DataManagementCommand *cmd)
{
    m_threadPool->start(cmd);
}

QUrl Nepomuk2::DataManagementAdaptor::decodeUri(const QString &s, bool namespaceAbbrExpansion) const
{
    if(namespaceAbbrExpansion) {
        if(m_namespacePrefixRx.exactMatch(s)) {
            const QString ns = m_namespacePrefixRx.cap(1);
            const QString name = m_namespacePrefixRx.cap(2);
            QHash<QString, QString>::const_iterator it = m_namespaces.constFind(ns);
            if(it != m_namespaces.constEnd()) {
                return QUrl::fromEncoded(QString(it.value() + name).toAscii());
            }
        }
    }

    // fallback
    return Nepomuk2::decodeUrl(s);
}

QList<QUrl> Nepomuk2::DataManagementAdaptor::decodeUris(const QStringList &urlStrings, bool namespaceAbbrExpansions) const
{
    QList<QUrl> urls;
    Q_FOREACH(const QString& urlString, urlStrings) {
        if(!urlString.isEmpty()) {
            urls << decodeUri(urlString, namespaceAbbrExpansions);
        }
    }
    return urls;
}

void Nepomuk2::DataManagementAdaptor::setPrefixes(const QHash<QString, QString>& prefixes)
{
    m_namespaces = prefixes;
}

void Nepomuk2::DataManagementAdaptor::importResources(const QString &url, const QString &serialization, int identificationMode, int flags, const QString &app)
{
    importResources(url, serialization, identificationMode, flags, PropertyHash(), app);
}

void Nepomuk2::DataManagementAdaptor::importResources(const QString &url, const QString &serialization, int identificationMode, int flags, const Nepomuk2::PropertyHash &additionalMetadata, const QString &app)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new ImportResourcesCommand(decodeUri(url), Soprano::mimeTypeToSerialization(serialization), serialization, identificationMode, flags, additionalMetadata, app, m_model, message()));
}

QString Nepomuk2::DataManagementAdaptor::exportResources(const QStringList &resources, const QString &mimeType, int flags, const QStringList &targetParties)
{
    Q_ASSERT(calledFromDBus());
    setDelayedReply(true);
    enqueueCommand(new ExportResourcesCommand(decodeUris(resources), Soprano::mimeTypeToSerialization(mimeType), mimeType, Nepomuk2::DescribeResourcesFlags(flags), decodeUris(targetParties), m_model, message()));
    // QtDBus will ignore this return value
    return QString();
}

void Nepomuk2::DataManagementAdaptor::clearCache()
{
    m_model->clearCache();
}

#include "datamanagementadaptor.moc"
