/*
    Copyright (c) 2010 Klarälvdalens Datakonsult AB,
                       a KDAB Group company <info@kdab.com>
    Author: Tobias Koenig <tokoe@kdab.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "removecollectionrecursivetask.h"

#include <akonadi/kmime/messageflags.h>
#include <kimap/deletejob.h>
#include <kimap/expungejob.h>
#include <kimap/selectjob.h>
#include <kimap/storejob.h>
#include <kimap/closejob.h>
#include <klocale.h>

Q_DECLARE_METATYPE( KIMAP::DeleteJob* )

RemoveCollectionRecursiveTask::RemoveCollectionRecursiveTask( ResourceStateInterface::Ptr resource, QObject *parent )
  : ResourceTask( CancelIfNoSession, resource, parent ),
    mSession( 0 ), mFolderFound( false )
{
}

RemoveCollectionRecursiveTask::~RemoveCollectionRecursiveTask()
{
}

void RemoveCollectionRecursiveTask::doStart( KIMAP::Session *session )
{
  mSession = session;

  mFolderFound = false;
  KIMAP::ListJob *listJob = new KIMAP::ListJob( session );
  listJob->setIncludeUnsubscribed( !isSubscriptionEnabled() );
  listJob->setQueriedNamespaces( serverNamespaces() );
  connect( listJob, SIGNAL(mailBoxesReceived(QList<KIMAP::MailBoxDescriptor>,QList<QList<QByteArray> >)),
           this, SLOT(onMailBoxesReceived(QList<KIMAP::MailBoxDescriptor>,QList<QList<QByteArray> >)) );
  connect( listJob, SIGNAL(result(KJob*)), SLOT(onJobDone(KJob*)) );
  listJob->start();
}

void RemoveCollectionRecursiveTask::onMailBoxesReceived( const QList< KIMAP::MailBoxDescriptor > &descriptors,
                                                         const QList< QList<QByteArray> >& )
{
  const QString mailBox = mailBoxForCollection( collection() );

  // We have to delete the deepest-nested folders first, so
  // we use a map here that has the level of nesting as key.
  QMultiMap<int, KIMAP::MailBoxDescriptor > foldersToDelete;

  for ( int i = 0; i < descriptors.size(); ++i ) {
    const KIMAP::MailBoxDescriptor descriptor = descriptors[ i ];

    if ( descriptor.name == mailBox || descriptor.name.startsWith( mailBox + descriptor.separator ) ) { // a sub folder to delete
      const QStringList pathParts = descriptor.name.split( descriptor.separator );
      foldersToDelete.insert( pathParts.count(), descriptor );
    }
  }

  if  ( foldersToDelete.isEmpty() ) {
      return;
  }

  mFolderFound = true;

  // Now start the actual deletion work
  mFolderIterator.reset( new QMapIterator<int, KIMAP::MailBoxDescriptor >( foldersToDelete ) );
  mFolderIterator->toBack(); // we start with largest nesting value first

  deleteNextMailbox();
}

void RemoveCollectionRecursiveTask::deleteNextMailbox()
{
  if ( !mFolderIterator->hasPrevious() ) {
      changeProcessed(); // finish the job
      return;
  }

  mFolderIterator->previous();
  const KIMAP::MailBoxDescriptor &descriptor = mFolderIterator->value();
  kDebug() << descriptor.name;

  // first select the mailbox
  KIMAP::SelectJob *selectJob = new KIMAP::SelectJob( mSession );
  selectJob->setMailBox( descriptor.name );
  connect( selectJob, SIGNAL(result(KJob*)), SLOT(onJobDone(KJob*)) );
  selectJob->start();

  // mark all items as deleted
  // This step shouldn't be required, but apparently some servers don't allow deleting, non empty mailboxes (although they should).
  KIMAP::ImapSet allItems;
  allItems.add( KIMAP::ImapInterval( 1, 0 ) ); // means 1:*
  KIMAP::StoreJob *storeJob = new KIMAP::StoreJob( mSession );
  storeJob->setSequenceSet( allItems );
  storeJob->setFlags( KIMAP::MessageFlags() << Akonadi::MessageFlags::Deleted );
  storeJob->setMode( KIMAP::StoreJob::AppendFlags );
  // The result is explicitly ignored, since this can fail in the case of an empty folder
  storeJob->start();

  // Some IMAP servers don't allow deleting an opened mailbox, so make sure
  // it's not opened (https://bugs.kde.org/show_bug.cgi?id=324932). CLOSE will
  // also trigger EXPUNGE to take care of the messages deleted above
  KIMAP::CloseJob *closeJob = new KIMAP::CloseJob( mSession );
  closeJob->setProperty( "folderDescriptor", descriptor.name );
  connect( closeJob, SIGNAL(result(KJob*)), SLOT(onCloseJobDone(KJob*)) );
  closeJob->start();
}

void RemoveCollectionRecursiveTask::onCloseJobDone( KJob* job )
{
  if ( job->error() ) {
    changeProcessed();
    kDebug( 5327 ) << "Failed to close the folder, resync the folder tree";
    emitWarning( i18n( "Failed to delete the folder, restoring folder list." ) );
    synchronizeCollectionTree();
  } else {
    KIMAP::DeleteJob *deleteJob = new KIMAP::DeleteJob( mSession );
    deleteJob->setMailBox( job->property( "folderDescriptor" ).toString() );
    connect( deleteJob, SIGNAL(result(KJob*)), SLOT(onDeleteJobDone(KJob*)) );
    deleteJob->start();
  }
}


void RemoveCollectionRecursiveTask::onDeleteJobDone( KJob* job )
{
  if ( job->error() ) {
    changeProcessed();

    kDebug( 5327 ) << "Failed to delete the folder, resync the folder tree";
    emitWarning( i18n( "Failed to delete the folder, restoring folder list." ) );
    synchronizeCollectionTree();
  } else {
    deleteNextMailbox();
  }
}

void RemoveCollectionRecursiveTask::onJobDone( KJob* job )
{
  if ( job->error() ) {
    changeProcessed();

    kDebug( 5327 ) << "Failed to delete the folder, resync the folder tree";
    emitWarning( i18n( "Failed to delete the folder, restoring folder list." ) );
    synchronizeCollectionTree();
  } else if ( !mFolderFound ) {
    changeProcessed();
    kDebug( 5327 ) << "Failed to find the folder to be deleted, resync the folder tree";
    emitWarning( i18n( "Failed to find the folder to be deleted, restoring folder list." ) );
    synchronizeCollectionTree();
  }
}

