/*
 *  resourceremote.cpp  -  KAlarm remote alarm calendar resource
 *  Program:  kalarm
 *  Copyright © 2006-2011 by David Jarvie <djarvie@kde.org>
 *  Based on resourceremote.cpp in kresources (updated to rev 721447),
 *  Copyright (c) 2003,2004 Cornelius Schumacher <schumacher@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, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include "resourceremote.moc"

//#include <libkdepim/progressmanager.h>
#include <kcal/calendarlocal.h>

#include <kio/job.h>
#include <kio/jobuidelegate.h>
#include <kio/jobclasses.h>
#include <klocale.h>
#include <kstandarddirs.h>

using namespace KCal;


KAResourceRemote::KAResourceRemote()
    : AlarmResource(),
      mDownloadJob(0),
      mUploadJob(0),
      mShowProgress(true),
      mUseCacheFile(true),
      mRemoteReadOnly(false)
{
    init();
}

KAResourceRemote::KAResourceRemote(const KConfigGroup& group)
    : AlarmResource(group),
      mDownloadJob(0),
      mUploadJob(0),
      mShowProgress(true),
      mUseCacheFile(true),
      mRemoteReadOnly(false)
{
    mDownloadUrl = KUrl(group.readEntry("DownloadUrl"));
    mUploadUrl = KUrl(group.readEntry("UploadUrl"));
    ResourceCached::readConfig(group );
    init();
}

KAResourceRemote::KAResourceRemote(CalEvent::Type type, const KUrl& downloadUrl, const KUrl& uploadUrl)
    : AlarmResource(type),
      mDownloadUrl(downloadUrl),
      mUploadUrl(uploadUrl.isEmpty() ? mDownloadUrl : uploadUrl),
      mDownloadJob(0),
      mUploadJob(0),
      mShowProgress(false),
      mUseCacheFile(false),
      mRemoteReadOnly(false)
{
    init();
}

void KAResourceRemote::init()
{
    setType("remote");   // set resource type
    lock(cacheFile());
}

KAResourceRemote::~KAResourceRemote()
{
    if (isOpen())
        close();
}

void KAResourceRemote::doClose()
{
    cancelDownload();
    if (mUploadJob)
    {
        mUploadJob->kill();
        mUploadJob = 0;
    }
    AlarmResource::doClose();
}

void KAResourceRemote::writeConfig(KConfigGroup& group)
{
    group.writeEntry("DownloadUrl", mDownloadUrl.url());
    group.writeEntry("UploadUrl", mUploadUrl.url());
    AlarmResource::writeConfig(group);
}

void KAResourceRemote::startReconfig()
{
    mNewDownloadUrl = mDownloadUrl;
    mNewUploadUrl   = mUploadUrl;
    AlarmResource::startReconfig();
}

void KAResourceRemote::applyReconfig()
{
    if (mReconfiguring)
    {
        AlarmResource::applyReconfig();
        if (setUrls(mNewDownloadUrl, mNewUploadUrl))
            mReconfiguring = 3;    // indicate that location has changed
        AlarmResource::applyReconfig();
    }
}

bool KAResourceRemote::readOnly() const
{
    return mRemoteReadOnly || AlarmResource::readOnly();
}

void KAResourceRemote::enableResource(bool enable)
{
    if (!enable)
        cancelDownload(false);
}

bool KAResourceRemote::doLoad(bool syncCache)
{
    if (mUploadJob)
        syncCache = false;   // still uploading, so the cache is up-to-date
    if (mDownloadJob)
    {
        kWarning(KARES_DEBUG) << "Download still in progress";
        return true;
    }
    mLoaded = false;
    emit invalidate(this);
    calendar()->close();
    setWrongAlarmType(false, false);
    clearChanges();
    if (!isActive())
    {
        updateCustomEvents(false);   // calendar is now empty
        return false;
    }
    mLoading = true;

    if (mUseCacheFile  ||  !syncCache)
    {
        disableChangeNotification();
        syncCache = !loadFromCache();    // if cache file doesn't exist yet, we need to download
        mUseCacheFile = false;
        enableChangeNotification();
    }
    emit resourceChanged(this);

    if (!syncCache)
    {
        kDebug(KARES_DEBUG) << mDownloadUrl.prettyUrl() << ": from cache";
        slotLoadJobResult(0);
    }
    else if (!lock()->lock())
    {
        kDebug(KARES_DEBUG) << mDownloadUrl.prettyUrl() << ": cache file is locked - something else must be loading the file";
        updateCustomEvents();
    }
    else
    {
        kDebug(KARES_DEBUG) << mDownloadUrl.prettyUrl() << ": downloading...";
        mDownloadJob = KIO::file_copy(mDownloadUrl, KUrl(cacheFile()), -1, KIO::Overwrite |
            ((mShowProgress && hasGui()) ? KIO::DefaultFlags : KIO::HideProgressInfo));
        connect(mDownloadJob, SIGNAL(result(KJob*)), SLOT(slotLoadJobResult(KJob*)));
#if 0
        if (mShowProgress  &&  hasGui())
        {
            connect(mDownloadJob, SIGNAL(percent(KJob*,ulong)),
                          SLOT(slotPercent(KJob*,ulong)));
            emit downloading(this, 0);
        }
#endif
    }
    return true;
}

void KAResourceRemote::slotPercent(KJob*, unsigned long percent)
{
    Q_UNUSED(percent);
#if 0
    emit downloading(this, percent);
#endif
}

void KAResourceRemote::slotLoadJobResult(KJob* job)
{
    bool err = false;
    if (job)
    {
        emit invalidate(this);
        calendar()->close();
        setWrongAlarmType(false, false);
        clearChanges();
        if (job->error())
        {
            if (hasGui())
            {
                KIO::FileCopyJob* j = qobject_cast<KIO::FileCopyJob*>(job);
                if (j)
                    j->ui()->showErrorMessage();
            }
            kError(KARES_DEBUG) << "Resource" << identifier() << " download error:" << job->errorString();
            setEnabled(false);
            err = true;
        }
        else
        {
            kDebug(KARES_DEBUG) << mDownloadUrl.prettyUrl() << ": success";
            setReloaded(true);    // the resource has now been downloaded at least once
            emit cacheDownloaded(this);
            disableChangeNotification();
            loadFromCache();
            enableChangeNotification();
        }

#if 0
        emit downloading(this, (unsigned long)-1);
#endif
    }
    mDownloadJob = 0;

    if (!err)
    {
        checkCompatibility(cacheFile());
        mLoaded = true;
    }
    mLoading = false;
    lock()->unlock();
    updateCustomEvents();
    emit loaded(this);
    if (job  &&  !err)
        emit resourceChanged(this);
}

void KAResourceRemote::cancelDownload(bool disable)
{
    if (mDownloadJob)
    {
        mDownloadJob->kill();
        mDownloadJob = 0;
        if (disable)
            setEnabled(false);
#if 0
        emit downloading(this, (unsigned long)-1);
#endif
        mLoading = false;
        lock()->unlock();
        updateCustomEvents();
        emit loaded(this);
    }
}

bool KAResourceRemote::doSave(bool syncCache)
{
    kDebug(KARES_DEBUG) << mUploadUrl.prettyUrl();
    if (readOnly()  ||  !hasChanges())
        return true;
    if (mDownloadJob)
    {
        kWarning(KARES_DEBUG) << "Download still in progress";
        return false;
    }
    if (mUploadJob)
    {
        kWarning(KARES_DEBUG) << "Upload still in progress";
        return false;
    }

    mChangedIncidences = allChanges();
    if (mCalIDFunction)
        (*mCalIDFunction)(*calendar());    // write the application ID into the calendar
    saveToCache();
    if (syncCache)
    {
        mUploadJob = KIO::file_copy(KUrl(cacheFile()), mUploadUrl, -1, KIO::Overwrite | (hasGui()?KIO::DefaultFlags:KIO::HideProgressInfo));
        connect(mUploadJob, SIGNAL(result(KJob*)), SLOT(slotSaveJobResult(KJob*)));
    }
    return true;
}

void KAResourceRemote::slotSaveJobResult(KJob* job)
{
    if (job->error())
    {
        if (hasGui())
        {
            KIO::FileCopyJob* j = qobject_cast<KIO::FileCopyJob*>(job);
            if (j)
                j->ui()->showErrorMessage();
        }
        kError(KARES_DEBUG) << "Resource" << identifier() << " upload error:" << job->errorString();
    }
    else
    {
        kDebug(KARES_DEBUG) << mUploadUrl.prettyUrl() << ": success";
        clearChanges();
    }

    mUploadJob = 0;
    emit resourceSaved(this);
    if (closeAfterSave())
        close();
}

bool KAResourceRemote::setUrls(const KUrl& downloadUrl, const KUrl& uploadUrl)
{
    if (mReconfiguring == 1)
    {
        mNewDownloadUrl = downloadUrl;
        mNewUploadUrl   = uploadUrl;
        return true;
    }
    if (downloadUrl.equals(mDownloadUrl)
    &&  uploadUrl.equals(mUploadUrl))
        return false;
    kDebug(KARES_DEBUG) << downloadUrl.prettyUrl() << "," << uploadUrl.prettyUrl();
    if (isOpen())
        close();
    bool active = isActive();
    if (active)
        enableResource(false);
    mDownloadUrl = downloadUrl;
    mUploadUrl   = uploadUrl;
    if (active)
        enableResource(true);
    // Trigger loading the new resource, and ensure that the new configuration is saved
    emit locationChanged(this);
    return true;
}

bool KAResourceRemote::setLocation(const QString& downloadUrl, const QString& uploadUrl)
{
    return setUrls(KUrl(downloadUrl), KUrl(uploadUrl));
}

QStringList KAResourceRemote::location() const
{
    return QStringList(downloadUrl().url()) << uploadUrl().url();
}

QString KAResourceRemote::displayLocation() const
{
    return mDownloadUrl.prettyUrl();
}

QString KAResourceRemote::displayType() const
{
    return i18nc("@info/plain", "URL");
}

// vim: et sw=4:
