/*
    Copyright (c) 2009 Stephen Kelly <steveire@gmail.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 "partfetcher.h"

#include "entitytreemodel.h"
#include "session.h"
#include "itemfetchjob.h"
#include "itemfetchscope.h"
#include <KLocalizedString>

#ifndef KDE_USE_FINAL
Q_DECLARE_METATYPE( QSet<QByteArray> )
#endif

using namespace Akonadi;

namespace Akonadi
{

class PartFetcherPrivate
{
  PartFetcherPrivate( PartFetcher *partFetcher, const QModelIndex &index, const QByteArray &partName )
      : m_persistentIndex( index ), m_partName( partName ), q_ptr( partFetcher )
  {
  }

  void fetchJobDone( KJob* );

  void modelDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight );

  QPersistentModelIndex m_persistentIndex;
  QByteArray m_partName;
  Item m_item;

  Q_DECLARE_PUBLIC( PartFetcher )
  PartFetcher *q_ptr;
};

}

void PartFetcherPrivate::fetchJobDone( KJob *job )
{
  Q_Q( PartFetcher );
  if ( job->error() ) {
    q->setError( KJob::UserDefinedError );
    q->setErrorText( i18n( "Unable to fetch item for index" ) );
    q->emitResult();
    return;
  }

  ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob*>( job );

  const Item::List list = fetchJob->items();

  Q_ASSERT( list.size() == 1 );

  // If m_persistentIndex comes from a selection proxy model, it could become
  // invalid if the user clicks around a lot.
  if ( !m_persistentIndex.isValid() ) {
    q->setError( KJob::UserDefinedError );
    q->setErrorText( i18n( "Index is no longer available" ) );
    q->emitResult();
    return;
  }

  const QSet<QByteArray> loadedParts = m_persistentIndex.data( EntityTreeModel::LoadedPartsRole ).value<QSet<QByteArray> >();

  Q_ASSERT( !loadedParts.contains( m_partName ) );

  Item item = m_persistentIndex.data( EntityTreeModel::ItemRole ).value<Item>();

  item.apply( list.at( 0 ) );

  QAbstractItemModel *model = const_cast<QAbstractItemModel *>( m_persistentIndex.model() );

  Q_ASSERT( model );

  QVariant itemVariant = QVariant::fromValue( item );
  model->setData( m_persistentIndex, itemVariant, EntityTreeModel::ItemRole );

  m_item = item;

  emit q->emitResult();
}

PartFetcher::PartFetcher( const QModelIndex &index, const QByteArray &partName, QObject *parent )
  : KJob( parent ), d_ptr( new PartFetcherPrivate( this, index, partName ) )
{
}

PartFetcher::~PartFetcher()
{
  delete d_ptr;
}

void PartFetcher::start()
{
  Q_D( PartFetcher );

  const QModelIndex index = d->m_persistentIndex;

  const QSet<QByteArray> loadedParts = index.data( EntityTreeModel::LoadedPartsRole ).value<QSet<QByteArray> >();

  if ( loadedParts.contains( d->m_partName ) ) {
    d->m_item = d->m_persistentIndex.data( EntityTreeModel::ItemRole ).value<Item>();
    emitResult();
    return;
  }

  const QSet<QByteArray> availableParts = index.data( EntityTreeModel::AvailablePartsRole ).value<QSet<QByteArray> >();
  if ( !availableParts.contains( d->m_partName ) ) {
    setError( UserDefinedError );
    setErrorText( i18n( "Payload part '%1' is not available for this index" , QString::fromLatin1( d->m_partName ) ) );
    emitResult();
    return;
  }

  Akonadi::Session *session = qobject_cast<Akonadi::Session *>( qvariant_cast<QObject *>( index.data( EntityTreeModel::SessionRole ) ) );

  if ( !session ) {
    setError( UserDefinedError );
    setErrorText( i18n( "No session available for this index" ) );
    emitResult();
    return;
  }

  const Akonadi::Item item = index.data( EntityTreeModel::ItemRole ).value<Akonadi::Item>();

  if ( !item.isValid() ) {
    setError( UserDefinedError );
    setErrorText( i18n( "No item available for this index" ) );
    emitResult();
    return;
  }

  ItemFetchScope scope;
  scope.fetchPayloadPart( d->m_partName );
  ItemFetchJob *itemFetchJob = new Akonadi::ItemFetchJob( item, session );
  itemFetchJob->setFetchScope( scope );

  connect( itemFetchJob, SIGNAL(result(KJob*)),
              this, SLOT(fetchJobDone(KJob*)) );
}

QModelIndex PartFetcher::index() const
{
  Q_D( const PartFetcher );

  return d->m_persistentIndex;
}

QByteArray PartFetcher::partName() const
{
  Q_D( const PartFetcher );

  return d->m_partName;
}

Item PartFetcher::item() const
{
  Q_D( const PartFetcher );

  return d->m_item;
}

#include "moc_partfetcher.cpp"
