/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2009-05-05
 * Description : file action manager
 *
 * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
 * Copyright (C) 2011-2012 by Gilles Caulier <caulier dot gilles at gmail dot 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, 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.
 *
 * ============================================================ */

#include "fileactionmngr.moc"
#include "fileactionmngr_p.h"

// Qt includes

#include <QMutexLocker>
#include <QPointer>

// KDE includes

#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>
#include <kprogressdialog.h>

// Local includes

#include "albumsettings.h"
#include "imageinfotasksplitter.h"
#include "loadingcacheinterface.h"
#include "metadatahub.h"
#include "metadatasettings.h"
#include "thumbnailloadthread.h"

namespace Digikam
{

class FileActionMngrCreator
{
public:

    FileActionMngr object;
};

K_GLOBAL_STATIC(FileActionMngrCreator, metadataManagercreator)

FileActionMngr* FileActionMngr::instance()
{
    return &metadataManagercreator->object;
}

FileActionMngr::FileActionMngr()
    : d(new FileActionMngrPriv(this))
{
    qRegisterMetaType<MetadataHub*>("MetadataHub*");
    qRegisterMetaType<FileActionImageInfoList>("FileActionImageInfoList");

    connect(d->fileWorker, SIGNAL(imageChangeFailed(QString, QStringList)),
            this, SIGNAL(signalImageChangeFailed(QString, QStringList)));
}

FileActionMngr::~FileActionMngr()
{
    shutDown();
    delete d;
}

bool FileActionMngr::requestShutDown()
{
    if (!isActive())
    {
        shutDown();
        return true;
    }

    QPointer<KProgressDialog> dialog = new KProgressDialog;
    dialog->progressBar()->setMinimum(0);
    dialog->progressBar()->setMaximum(0);
    dialog->setAllowCancel(true);
    dialog->setMinimumDuration(100);
    dialog->setLabelText(i18nc("@label", "Finishing tasks"));

    connect(d, SIGNAL(signalTasksFinished()),
            dialog, SLOT(accept()));

    dialog->exec();
    // Either, we finished and all is fine, or the user cancelled and we kill
    shutDown();
    return true;
}

void FileActionMngr::shutDown()
{
    d->dbWorker->deactivate();
    d->fileWorker->deactivate();
    d->dbWorker->wait();
    d->fileWorker->wait();
}

bool FileActionMngr::isActive()
{
    return d->isActive();
}

void FileActionMngr::assignTags(const QList<qlonglong>& ids, const QList<int>& tagIDs)
{
    assignTags(ImageInfoList(ids), tagIDs);
}

void FileActionMngr::assignTag(const ImageInfo& info, int tagID)
{
    assignTags(QList<ImageInfo>() << info, QList<int>() << tagID);
}

void FileActionMngr::assignTag(const QList<ImageInfo>& infos, int tagID)
{
    assignTags(infos, QList<int>() << tagID);
}

void FileActionMngr::assignTags(const ImageInfo& info, const QList<int>& tagIDs)
{
    assignTags(QList<ImageInfo>() << info, tagIDs);
}

void FileActionMngr::assignTags(const QList<ImageInfo>& infos, const QList<int>& tagIDs)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Assigning image tags"), d->dbProgressCreator());
    d->assignTags(taskList, tagIDs);
}

void FileActionMngr::removeTag(const ImageInfo& info, int tagID)
{
    removeTags(QList<ImageInfo>() << info, QList<int>() << tagID);
}

void FileActionMngr::removeTag(const QList<ImageInfo>& infos, int tagID)
{
    removeTags(infos, QList<int>() << tagID);
}

void FileActionMngr::removeTags(const ImageInfo& info, const QList<int>& tagIDs)
{
    removeTags(QList<ImageInfo>() << info, tagIDs);
}

void FileActionMngr::removeTags(const QList<ImageInfo>& infos, const QList<int>& tagIDs)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Removing image tags"), d->dbProgressCreator());
    d->removeTags(taskList, tagIDs);
}

void FileActionMngr::assignPickLabel(const ImageInfo& info, int pickId)
{
    assignPickLabel(QList<ImageInfo>() << info, pickId);
}

void FileActionMngr::assignColorLabel(const ImageInfo& info, int colorId)
{
    assignColorLabel(QList<ImageInfo>() << info, colorId);
}

void FileActionMngr::assignPickLabel(const QList<ImageInfo>& infos, int pickId)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Assigning image pick label"), d->dbProgressCreator());
    d->assignPickLabel(taskList, pickId);
}

void FileActionMngr::assignColorLabel(const QList<ImageInfo>& infos, int colorId)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Assigning image color label"), d->dbProgressCreator());
    d->assignColorLabel(taskList, colorId);
}

void FileActionMngr::assignRating(const ImageInfo& info, int rating)
{
    assignRating(QList<ImageInfo>() << info, rating);
}

void FileActionMngr::assignRating(const QList<ImageInfo>& infos, int rating)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Assigning image ratings"), d->dbProgressCreator());
    d->assignRating(taskList, rating);
}

void FileActionMngr::addToGroup(const ImageInfo& pick, const QList<ImageInfo>& infos)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Editing group"), d->dbProgressCreator());
    d->editGroup(AddToGroup, pick, taskList);
}

void FileActionMngr::removeFromGroup(const ImageInfo& info)
{
    removeFromGroup(QList<ImageInfo>() << info);
}

void FileActionMngr::removeFromGroup(const QList<ImageInfo>& infos)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Editing group"), d->dbProgressCreator());
    d->editGroup(RemoveFromGroup, ImageInfo(), taskList);
}

void FileActionMngr::ungroup(const ImageInfo& info)
{
    ungroup(QList<ImageInfo>() << info);
}

void FileActionMngr::ungroup(const QList<ImageInfo>& infos)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Editing group"), d->dbProgressCreator());
    d->editGroup(Ungroup, ImageInfo(), taskList);
}

void FileActionMngr::setExifOrientation(const QList<ImageInfo>& infos, int orientation)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Updating orientation in database"), d->dbProgressCreator());
    d->setExifOrientation(taskList, orientation);
}

void FileActionMngr::applyMetadata(const QList<ImageInfo>& infos, const MetadataHub& hub)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Applying metadata"), d->dbProgressCreator());
    // create hub parent-less, we will need to clone() it in a thread,
    // and that does not work with parent in a different thread
    d->applyMetadata(taskList, new MetadataHubOnTheRoad(hub));
}

void FileActionMngr::applyMetadata(const QList<ImageInfo>& infos, const MetadataHubOnTheRoad& hub)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Applying metadata"), d->dbProgressCreator());
    d->applyMetadata(taskList, new MetadataHubOnTheRoad(hub));
}

void FileActionMngr::applyMetadata(const QList<ImageInfo>& infos, MetadataHubOnTheRoad* hub)
{
    if (hub->parent())
    {
        kDebug() << "MetadataHubOnTheRoad object must not have a QObject parent";
        delete hub;
        return;
    }
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForDB(i18n("Applying metadata"), d->dbProgressCreator());
    d->applyMetadata(taskList, hub);
}

void FileActionMngr::transform(const QList<ImageInfo>& infos, KExiv2Iface::RotationMatrix::TransformationAction action)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(infos);
    taskList.schedulingForWrite(i18n("Rotating images"), d->fileProgressCreator());
    for (ImageInfoTaskSplitter splitter(taskList); splitter.hasNext();)
        d->transform(splitter.next(), action);
}

void FileActionMngr::copyAttributes(const ImageInfo& source, const QString& derivedPath)
{
    copyAttributes(source, QStringList() << derivedPath);
}

void FileActionMngr::copyAttributes(const ImageInfo& source, const QStringList& derivedPaths)
{
    FileActionImageInfoList taskList = FileActionImageInfoList::create(QList<ImageInfo>() << source);
    taskList.schedulingForDB(i18n("Copying attributes"), d->dbProgressCreator());
    d->copyAttributes(taskList, derivedPaths);
}


} // namespace Digikam
