/**
 * 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 TQt library by Trolltech AS, Norway (or with modified versions
 *  of TQt that use the same license as TQt), 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
 *  TQt.  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 "kmfolder.h"
#include "globalsettings.h"
#include "folderstorage.h"
#include "broadcaststatus.h"
using KPIM::BroadcastStatus;
#include "kmcommands.h"

#include <kdebug.h>
#include <tdelocale.h>

using namespace KMail;

// Look at this number of messages in each slotDoWork call
#define EXPIREJOB_NRMESSAGES 100
// And wait this number of milliseconds before calling it again
#define EXPIREJOB_TIMERINTERVAL 100

/*
 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()]
*/


ExpireJob::ExpireJob( KMFolder* folder, bool immediate )
 : ScheduledJob( folder, immediate ), mTimer( this ), mCurrentIndex( 0 ),
   mFolderOpen( false ), mMoveToFolder( 0 )
{
}

ExpireJob::~ExpireJob()
{
}

void ExpireJob::kill()
{
  Q_ASSERT( mCancellable );
  // We must close the folder if we opened it and got interrupted
  if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
    mSrcFolder->storage()->close( "expirejob" );
  FolderJob::kill();
}

void ExpireJob::execute()
{
  mMaxUnreadTime = -1;
  mMaxReadTime = -1;
  mCurrentIndex = 0;

  int unreadDays, readDays;
  mSrcFolder->daysToExpire( unreadDays, readDays );
  if (unreadDays >= 0) {
    kdDebug(5006) << "ExpireJob: deleting unread older than "<< unreadDays << " days" << endl;
    mMaxUnreadTime = time(0) - unreadDays * 3600 * 24;
  }
  if (readDays >= 0) {
    kdDebug(5006) << "ExpireJob: deleting read older than "<< readDays << " days" << endl;
    mMaxReadTime = time(0) - readDays * 3600 * 24;
  }

  if ((mMaxUnreadTime == 0) && (mMaxReadTime == 0)) {
    kdDebug(5006) << "ExpireJob: nothing to do" << endl;
    delete this;
    return;
  }

  FolderStorage* storage = mSrcFolder->storage();
  mOpeningFolder = true; // Ignore open-notifications while opening the folder
  storage->open( "expirejob" );
  mOpeningFolder = false;
  mFolderOpen = true;
  mCurrentIndex = storage->count()-1;
  kdDebug(5006) << "ExpireJob: starting to expire in folder " << mSrcFolder->location() << endl;
  connect( &mTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( slotDoWork() ) );
  mTimer.start( EXPIREJOB_TIMERINTERVAL );
  slotDoWork();
  // do nothing here, we might be deleted!
}

void ExpireJob::slotDoWork()
{
  // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
  FolderStorage* storage = mSrcFolder->storage();
  int stopIndex = mImmediate ? 0 : TQMAX( 0, mCurrentIndex - EXPIREJOB_NRMESSAGES );
#ifdef DEBUG_SCHEDULER
  kdDebug(5006) << "ExpireJob: checking messages " << mCurrentIndex << " to " << stopIndex << endl;
#endif
  for( ; mCurrentIndex >= stopIndex; mCurrentIndex-- ) {
    const KMMsgBase *mb = storage->getMsgBase( mCurrentIndex );
#ifdef DEBUG_SCHEDULER
    kdDebug(5006) << "ExpireJob: checking message " << mCurrentIndex << " existence" << endl;
#endif
    if (mb == 0)
      continue;
#ifdef DEBUG_SCHEDULER
    kdDebug(5006) << "ExpireJob: checking message " << mCurrentIndex << " importance" << endl;
#endif
    if ( ( mb->isImportant() || mb->isTodo() || mb->isWatched() )
      && GlobalSettings::self()->excludeImportantMailFromExpiry() )
       continue;

#ifdef DEBUG_SCHEDULER
    kdDebug(5006) << "ExpireJob: checking message " << mCurrentIndex << " time" << endl;
#endif
    time_t maxTime = mb->isUnread() ? mMaxUnreadTime : mMaxReadTime;

#ifdef DEBUG_SCHEDULER
    kdDebug(5006) << "ExpireJob: checking message " << mCurrentIndex << " time (" << mb->date() << " vs " << maxTime << ")" << endl;
#endif
    if (mb->date() < maxTime) {
      kdDebug(5006) << "ExpireJob: expiring message " << mCurrentIndex << " from folder " << mSrcFolder->location() << endl;
      mRemovedMsgs.append( storage->getMsgBase( mCurrentIndex ) );
    }
  }
  if ( stopIndex == 0 )
    done();
}

void ExpireJob::done()
{
  mTimer.stop();

  TQString str;
  bool moving = false;

  if ( !mRemovedMsgs.isEmpty() ) {
    int count = mRemovedMsgs.count();
    // The command shouldn't kill us because it opens the folder
    mCancellable = false;
    if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
      // Expire by deletion, i.e. move to null target folder
      kdDebug(5006) << "ExpireJob: finished expiring in folder "
                    << mSrcFolder->location()
                    << " " << count << " messages to remove." << endl;
      KMMoveCommand* cmd = new KMMoveCommand( 0, mRemovedMsgs );
      connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
               this, TQ_SLOT( slotMessagesMoved( KMCommand * ) ) );
      cmd->start();
      moving = true;
      str = i18n( "Removing 1 old message from folder %1...",
                  "Removing %n old messages from folder %1...", count )
            .arg( mSrcFolder->label() );
    } else {
      // Expire by moving
      mMoveToFolder =
        kmkernel->findFolderById( mSrcFolder->expireToFolderId() );
      if ( !mMoveToFolder ) {
        str = i18n( "Cannot expire messages from folder %1: destination "
                    "folder %2 not found" )
              .arg( mSrcFolder->label(), mSrcFolder->expireToFolderId() );
        kdWarning(5006) << str << endl;
      } else {
        kdDebug(5006) << "ExpireJob: finished expiring in folder "
                      << mSrcFolder->location() << " "
                      << mRemovedMsgs.count() << " messages to move to "
                      << mMoveToFolder->label() << endl;
        KMMoveCommand* cmd = new KMMoveCommand( mMoveToFolder, mRemovedMsgs );
        connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
                 this, TQ_SLOT( slotMessagesMoved( KMCommand * ) ) );
        cmd->start();
        moving = true;
        str = i18n( "Moving 1 old message from folder %1 to folder %2...",
                    "Moving %n old messages from folder %1 to folder %2...",
                    count )
              .arg( mSrcFolder->label(), mMoveToFolder->label() );
      }
    }
  }
  if ( !str.isEmpty() )
    BroadcastStatus::instance()->setStatusMsg( str );

  TDEConfigGroup group( KMKernel::config(), "Folder-" + mSrcFolder->idString() );
  group.writeEntry( "Current", -1 ); // i.e. make it invalid, the serial number will be used

  if ( !moving ) {
    mSrcFolder->storage()->close( "expirejob" );
    mFolderOpen = false;
    delete this;
  }
}

void ExpireJob::slotMessagesMoved( KMCommand *command )
{
  mSrcFolder->storage()->close( "expirejob" );
  mFolderOpen = false;
  TQString msg;
  switch ( command->result() ) {
  case KMCommand::OK:
    if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
      msg = i18n( "Removed 1 old message from folder %1.",
                  "Removed %n old messages from folder %1.",
                  mRemovedMsgs.count() )
            .arg( mSrcFolder->label() );
    }
    else {
      msg = i18n( "Moved 1 old message from folder %1 to folder %2.",
                  "Moved %n old messages from folder %1 to folder %2.",
                  mRemovedMsgs.count() )
            .arg( mSrcFolder->label(), mMoveToFolder->label() );
    }
    break;
  case KMCommand::Failed:
    if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
      msg = i18n( "Removing old messages from folder %1 failed." )
            .arg( mSrcFolder->label() );
    }
    else {
      msg = i18n( "Moving old messages from folder %1 to folder %2 failed." )
            .arg( mSrcFolder->label(), mMoveToFolder->label() );
    }
    break;
  case KMCommand::Canceled:
    if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
      msg = i18n( "Removing old messages from folder %1 was canceled." )
            .arg( mSrcFolder->label() );
    }
    else {
      msg = i18n( "Moving old messages from folder %1 to folder %2 was "
                  "canceled." )
            .arg( mSrcFolder->label(), mMoveToFolder->label() );
    }
  default: ;
  }
  BroadcastStatus::instance()->setStatusMsg( msg );

  deleteLater();
}

#include "expirejob.moc"
