// vim: set tabstop=4 shiftwidth=4 expandtab:
/*
Gwenview: an image viewer
Copyright 2009 Aurélien Gâteau <agateau@kde.org>

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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.

*/
// Self
#include "fileutils.h"

// Qt
#include <QBuffer>
#include <QFile>
#include <QFileInfo>
#include <QScopedPointer>
#include <QUrl>

// KF
#include <KFileItem>
#include <KIO/CopyJob>
#include <KIO/Job>
#include <KIO/JobUiDelegate>
#include <KJobWidgets>
#include <kio/jobclasses.h>

// Local
#include "gwenview_importer_debug.h"

namespace Gwenview
{
namespace FileUtils
{
bool contentsAreIdentical(const QUrl &url1, const QUrl &url2, QWidget *authWindow)
{
    KIO::StatJob *statJob;
    KIO::StoredTransferJob *fileJob;
    QScopedPointer<QIODevice> file1, file2;
    QByteArray file1Bytes, file2Bytes;

    if (url1.isLocalFile()) {
        statJob = KIO::mostLocalUrl(url1);
        KJobWidgets::setWindow(statJob, authWindow);
        if (!statJob->exec()) {
            qCWarning(GWENVIEW_IMPORTER_LOG) << "Unable to stat" << url1;
            return false;
        }
        file1.reset(new QFile(statJob->mostLocalUrl().toLocalFile()));
    } else { // file1 is remote
        fileJob = KIO::storedGet(url1, KIO::NoReload, KIO::HideProgressInfo);
        KJobWidgets::setWindow(fileJob, authWindow);
        if (!fileJob->exec()) {
            qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url1;
            return false;
        }
        file1Bytes = QByteArray(fileJob->data());
        file1.reset(new QBuffer(&file1Bytes));
    }
    if (!file1->open(QIODevice::ReadOnly)) {
        // Can't read url1, assume it's different from url2
        qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url1;
        return false;
    }

    if (url2.isLocalFile()) {
        statJob = KIO::mostLocalUrl(url2);
        KJobWidgets::setWindow(statJob, authWindow);
        if (!statJob->exec()) {
            qCWarning(GWENVIEW_IMPORTER_LOG) << "Unable to stat" << url2;
            return false;
        }
        file2.reset(new QFile(statJob->mostLocalUrl().toLocalFile()));
    } else { // file2 is remote
        fileJob = KIO::storedGet(url2, KIO::NoReload, KIO::HideProgressInfo);
        KJobWidgets::setWindow(fileJob, authWindow);
        if (!fileJob->exec()) {
            qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url2;
            return false;
        }
        file2Bytes = QByteArray(fileJob->data());
        file2.reset(new QBuffer(&file2Bytes));
    }
    if (!file2->open(QIODevice::ReadOnly)) {
        // Can't read url2, assume it's different from url1
        qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url2;
        return false;
    }

    const int CHUNK_SIZE = 4096;
    while (!file1->atEnd() && !file2->atEnd()) {
        QByteArray url1Array = file1->read(CHUNK_SIZE);
        QByteArray url2Array = file2->read(CHUNK_SIZE);

        if (url1Array != url2Array) {
            return false;
        }
    }
    if (file1->atEnd() && file2->atEnd()) {
        return true;
    } else {
        qCWarning(GWENVIEW_IMPORTER_LOG) << "One file ended before the other";
        return false;
    }
}

RenameResult rename(const QUrl &src, const QUrl &dst_, QWidget *authWindow)
{
    QUrl dst = dst_;
    RenameResult result = RenamedOK;
    int count = 1;

    QFileInfo fileInfo(dst.fileName());
    QString prefix = fileInfo.completeBaseName() + '_';
    QString suffix = '.' + fileInfo.suffix();

    // Get src size
    KIO::StatJob *sourceStat = KIO::stat(src);
    KJobWidgets::setWindow(sourceStat, authWindow);
    if (!sourceStat->exec()) {
        return RenameFailed;
    }
    KFileItem item(sourceStat->statResult(), src, true /* delayedMimeTypes */);
    KIO::filesize_t srcSize = item.size();

    // Find unique name
    KIO::StatJob *statJob = KIO::stat(dst);
    KJobWidgets::setWindow(statJob, authWindow);
    while (statJob->exec()) {
        // File exists. If it's not the same, try to create a new name
        item = KFileItem(statJob->statResult(), dst, true /* delayedMimeTypes */);
        KIO::filesize_t dstSize = item.size();

        if (srcSize == dstSize && contentsAreIdentical(src, dst, authWindow)) {
            // Already imported, skip it
            KIO::Job *job = KIO::file_delete(src, KIO::HideProgressInfo);
            KJobWidgets::setWindow(job, authWindow);

            return Skipped;
        }
        result = RenamedUnderNewName;

        dst.setPath(dst.adjusted(QUrl::RemoveFilename).path() + prefix + QString::number(count) + suffix);
        statJob = KIO::stat(dst);
        KJobWidgets::setWindow(statJob, authWindow);

        ++count;
    }

    // Rename
    KIO::Job *job = KIO::moveAs(src, dst, KIO::HideProgressInfo);
    KJobWidgets::setWindow(job, authWindow);
    if (!job->exec()) {
        result = RenameFailed;
    }
    return result;
}

} // namespace
} // namespace
