/**
 * Copyright (c) 2004 David Faure <faure@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; version 2 of the License
 *
 *  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.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the Qt library by Trolltech AS, Norway (or with modified versions
 *  of Qt that use the same license as Qt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  Qt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */
#include "expirejob.h"
#include "expirecollectionattribute.h"
#include "mailkernel.h"

#include <libkdepim/broadcaststatus.h>
using KPIM::BroadcastStatus;

#include <KDebug>
#include <KLocale>

#include <Akonadi/ItemDeleteJob>
#include <Akonadi/ItemModifyJob>
#include <Akonadi/ItemFetchJob>
#include <Akonadi/ItemFetchScope>
#include <Akonadi/ItemMoveJob>
#include <Akonadi/KMime/MessageParts>
#include <Akonadi/KMime/MessageStatus>
#include <akonadi/kmime/messageflags.h>
#include <KMime/Message>

/*
 Testcases for folder expiry:
  Automatic expiry:
  - normal case (ensure folder has old mails and expiry settings, wait for auto-expiry)
  - having the folder selected when the expiry job would run (gets delayed)
  - selecting a folder while an expiry job is running for it (should interrupt)
  - exiting kmail while an expiry job is running (should abort & delete things cleanly)
  Manual expiry:
  - RMB / expire (for one folder)                   [KMMainWidget::slotExpireFolder()]
  - RMB on Local Folders / Expire All Folders       [KMFolderMgr::expireAll()]
  - Expire All Folders                              [KMMainWidget::slotExpireAll()]
*/

namespace MailCommon {

ExpireJob::ExpireJob( const Akonadi::Collection &folder, bool immediate )
  : ScheduledJob( folder, immediate ), mMoveToFolder( 0 )
{
}

ExpireJob::~ExpireJob()
{
  kDebug();
}

void ExpireJob::kill()
{
  ScheduledJob::kill();
}

void ExpireJob::execute()
{
  mMaxUnreadTime = 0;
  mMaxReadTime = 0;
  int unreadDays, readDays;
  bool mustDeleteExpirationAttribute = false;

  MailCommon::ExpireCollectionAttribute *expirationAttribute =
    MailCommon::ExpireCollectionAttribute::expirationCollectionAttribute(
      mSrcFolder, mustDeleteExpirationAttribute );

  expirationAttribute->daysToExpire( unreadDays, readDays );
  if ( mustDeleteExpirationAttribute ) {
    delete expirationAttribute;
  }

  if ( unreadDays > 0 ) {
    kDebug() << "ExpireJob: deleting unread older than"<< unreadDays << "days";
    mMaxUnreadTime = time(0) - unreadDays * 3600 * 24;
  }
  if ( readDays > 0 ) {
    kDebug() << "ExpireJob: deleting read older than"<< readDays << "days";
    mMaxReadTime = time(0) - readDays * 3600 * 24;
  }

  if ( ( mMaxUnreadTime == 0 ) && ( mMaxReadTime == 0 ) ) {
    kDebug() << "ExpireJob: nothing to do";
    deleteLater();
    return;
  }
  kDebug() << "ExpireJob: starting to expire in folder" << mSrcFolder.name();
  slotDoWork();
  // do nothing here, we might be deleted!
}

void ExpireJob::slotDoWork()
{
  Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( mSrcFolder, this );
  job->fetchScope().fetchPayloadPart( Akonadi::MessagePart::Envelope );
  connect( job, SIGNAL(result(KJob*)), SLOT(itemFetchResult(KJob*)) );
}

void ExpireJob::itemFetchResult( KJob *job )
{
  if ( job->error() ) {
    kWarning() << job->errorString();
    deleteLater();
    return;
  }

  foreach ( const Akonadi::Item &item, qobject_cast<Akonadi::ItemFetchJob*>( job )->items() ) {
    if ( !item.hasPayload<KMime::Message::Ptr>() ) {
      continue;
    }

    const KMime::Message::Ptr mb = item.payload<KMime::Message::Ptr>();
    Akonadi::MessageStatus status;
    status.setStatusFromFlags( item.flags() );
    if ( ( status.isImportant() || status.isToAct() || status.isWatched() ) &&
         SettingsIf->excludeImportantMailFromExpiry() ) {
       continue;
    }

    time_t maxTime = status.isRead() ? mMaxReadTime : mMaxUnreadTime;

    if ( !mb->date( false ) ) {
      continue;
    }

    if ( mb->date()->dateTime().dateTime().toTime_t() < maxTime ) {
      mRemovedMsgs.append( item );
    }
  }

  done();
}

void ExpireJob::done()
{
  QString str;
  bool moving = false;

  if ( !mRemovedMsgs.isEmpty() ) {
    int count = mRemovedMsgs.count();

    // The command shouldn't kill us because it opens the folder
    mCancellable = false;
    bool mustDeleteExpirationAttribute = false;

    MailCommon::ExpireCollectionAttribute *expirationAttribute =
      MailCommon::ExpireCollectionAttribute::expirationCollectionAttribute(
        mSrcFolder, mustDeleteExpirationAttribute );

    if ( expirationAttribute->expireAction() == MailCommon::ExpireCollectionAttribute::ExpireDelete ) {
      // Expire by deletion, i.e. move to null target folder
      kDebug() << "ExpireJob: finished expiring in folder"
               << mSrcFolder.name()
               << count << "messages to remove.";
      Akonadi::ItemDeleteJob *job = new Akonadi::ItemDeleteJob( mRemovedMsgs, this );
      connect( job, SIGNAL(result(KJob*)), this, SLOT(slotExpireDone(KJob*)) );
      moving = true;
      str = i18np( "Removing 1 old message from folder %2...",
                   "Removing %1 old messages from folder %2...",
                   count, mSrcFolder.name() );
    } else {
      // Expire by moving
      mMoveToFolder = Kernel::self()->collectionFromId( expirationAttribute->expireToFolderId() );
      if ( !mMoveToFolder.isValid() ) {
        str = i18n( "Cannot expire messages from folder %1: destination "
                    "folder %2 not found",
                    mSrcFolder.name(), expirationAttribute->expireToFolderId() );
        kWarning() << str;
      } else {
        kDebug() << "ExpireJob: finished expiring in folder"
                 << mSrcFolder.name()
                 << mRemovedMsgs.count() << "messages to move to"
                 << mMoveToFolder.name();
        Akonadi::ItemMoveJob *job = new Akonadi::ItemMoveJob( mRemovedMsgs, mMoveToFolder, this );
        connect( job, SIGNAL(result(KJob*)), this, SLOT(slotMoveDone(KJob*)) );
        moving = true;
        str = i18np( "Moving 1 old message from folder %2 to folder %3...",
                     "Moving %1 old messages from folder %2 to folder %3...",
                     count, mSrcFolder.name(), mMoveToFolder.name() );
      }
    }
    if ( mustDeleteExpirationAttribute ) {
      delete expirationAttribute;
    }
  }
  if ( !str.isEmpty() ) {
    BroadcastStatus::instance()->setStatusMsg( str );
  }

  if ( !moving ) {
    deleteLater();
  }
}

void ExpireJob::slotMoveDone( KJob *job )
{
  if ( job->error() ) {
    kError() << job->error() << job->errorString();
  }
  Akonadi::ItemMoveJob *itemjob = dynamic_cast<Akonadi::ItemMoveJob *>( job );
  if ( itemjob ) {
    QList<Akonadi::Item> lst = itemjob->items();
    if ( !lst.isEmpty() ) {
      QList<Akonadi::Item> newLst;
      Q_FOREACH( Akonadi::Item item, lst ) {
	if(!item.hasFlag(Akonadi::MessageFlags::Seen) ) {
           item.setFlag( Akonadi::MessageFlags::Seen );
           newLst<<item;
	}
      }
      if( !newLst.isEmpty() ) {
         Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob( newLst, this );
         modifyJob->disableRevisionCheck();
         connect( modifyJob, SIGNAL(result(KJob*)), this, SLOT(slotExpireDone(KJob*)) );
      } else {
         slotExpireDone( job );
      }
    }
  } else {
    slotExpireDone( job );
  }
}

void ExpireJob::slotExpireDone( KJob *job )
{
  if ( job->error() ) {
    kError() << job->error() << job->errorString();
  }

  QString msg;
  int error = job->error();
  bool mustDeleteExpirationAttribute = false;

  MailCommon::ExpireCollectionAttribute *expirationAttribute =
    MailCommon::ExpireCollectionAttribute::expirationCollectionAttribute(
      mSrcFolder, mustDeleteExpirationAttribute );

  switch ( error ) {
  case KJob::NoError:
    if ( expirationAttribute->expireAction() == MailCommon::ExpireCollectionAttribute::ExpireDelete ) {
      msg = i18np( "Removed 1 old message from folder %2.",
                   "Removed %1 old messages from folder %2.",
                   mRemovedMsgs.count(),
                   mSrcFolder.name() );
    } else {
      msg = i18np( "Moved 1 old message from folder %2 to folder %3.",
                   "Moved %1 old messages from folder %2 to folder %3.",
                   mRemovedMsgs.count(), mSrcFolder.name(), mMoveToFolder.name() );
    }
    break;

  case Akonadi::Job::UserCanceled:
    if ( expirationAttribute->expireAction() == MailCommon::ExpireCollectionAttribute::ExpireDelete ) {
      msg = i18n( "Removing old messages from folder %1 was canceled.",
                  mSrcFolder.name() );
    } else {
      msg = i18n( "Moving old messages from folder %1 to folder %2 was "
                  "canceled.",
                  mSrcFolder.name(), mMoveToFolder.name() );
    }

  default: //any other error
    if ( expirationAttribute->expireAction() == MailCommon::ExpireCollectionAttribute::ExpireDelete ) {
      msg = i18n( "Removing old messages from folder %1 failed.",
                  mSrcFolder.name() );
    } else {
      msg = i18n( "Moving old messages from folder %1 to folder %2 failed.",
                  mSrcFolder.name(), mMoveToFolder.name() );
    }
    break;
  }

  BroadcastStatus::instance()->setStatusMsg( msg );
  if ( mustDeleteExpirationAttribute ) {
    delete expirationAttribute;
  }
  deleteLater();
}

}

#include "expirejob.moc"
