/*
    Copyright (C) 2010 Klarälvdalens Datakonsult AB,
        a KDAB Group company, info@kdab.net,
        author Stephen Kelly <stephen@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 "entityorderproxymodel.h"

#include <QMimeData>

#include <KDE/KConfigGroup>

#include <Akonadi/Collection>
#include <Akonadi/Item>
#include <Akonadi/EntityTreeModel>

namespace Akonadi
{

class EntityOrderProxyModelPrivate
{
public:
  EntityOrderProxyModelPrivate( EntityOrderProxyModel *qq )
    : q_ptr( qq )
  {

  }

  void saveOrder( const QModelIndex &index );

  KConfigGroup m_orderConfig;

  Q_DECLARE_PUBLIC(EntityOrderProxyModel)
  EntityOrderProxyModel * const q_ptr;

};

}

using namespace Akonadi;

EntityOrderProxyModel::EntityOrderProxyModel( QObject* parent )
  : QSortFilterProxyModel(parent), d_ptr( new EntityOrderProxyModelPrivate( this ) )
{
  setDynamicSortFilter(true);
}

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

void EntityOrderProxyModel::setOrderConfig( KConfigGroup& configGroup )
{
  Q_D( EntityOrderProxyModel );
  layoutAboutToBeChanged();
  d->m_orderConfig = configGroup;
  layoutChanged();
}

bool EntityOrderProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
{
  Q_D( const EntityOrderProxyModel );

  if ( !d->m_orderConfig.isValid() )
    return QSortFilterProxyModel::lessThan( left, right );

  Collection col = left.data( EntityTreeModel::ParentCollectionRole ).value<Collection>();

  if ( !d->m_orderConfig.hasKey( QString::number( col.id() ) ) )
    return QSortFilterProxyModel::lessThan( left, right );

  QStringList list = d->m_orderConfig.readEntry( QString::number( col.id() ), QStringList() );

  if ( list.isEmpty() )
    return QSortFilterProxyModel::lessThan( left, right );

  QString leftValue = configString( left );
  QString rightValue = configString( right );

  int leftPosition = list.indexOf( leftValue );
  int rightPosition = list.indexOf( rightValue );

  if ( leftPosition < 0 || rightPosition < 0 )
    return QSortFilterProxyModel::lessThan( left, right );

  return leftPosition < rightPosition;
}

bool EntityOrderProxyModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
{
  Q_D( EntityOrderProxyModel );

  if ( !d->m_orderConfig.isValid() )
    return QSortFilterProxyModel::dropMimeData( data, action, row, column, parent );

  if ( !data->hasFormat( QLatin1String( "text/uri-list" ) ) )
    return QSortFilterProxyModel::dropMimeData( data, action, row, column, parent );

  if ( row == -1 )
    return QSortFilterProxyModel::dropMimeData( data, action, row, column, parent );

  bool containsMove = false;

  const KUrl::List urls = KUrl::List::fromMimeData( data );

  Collection parentCol;

  if (parent.isValid())
    parentCol = parent.data( EntityTreeModel::CollectionRole ).value<Collection>();
  else
  {
    if (!hasChildren(parent))
      return QSortFilterProxyModel::dropMimeData( data, action, row, column, parent );

    const QModelIndex targetIndex = index( 0, column, parent );

    parentCol = targetIndex.data( EntityTreeModel::ParentCollectionRole ).value<Collection>();
  }

  QStringList droppedList;
  foreach ( const KUrl &url, urls ) {
    Collection col = Collection::fromUrl( url );

    if ( !col.isValid() )
    {
      Item item = Item::fromUrl( url );
      if ( !item.isValid() )
        continue;

      const QModelIndexList list = EntityTreeModel::modelIndexesForItem( this, item );
      if ( list.isEmpty() )
        continue;

      if ( !containsMove && list.first().data( EntityTreeModel::ParentCollectionRole ).value<Collection>().id() != parentCol.id() )
        containsMove = true;

      droppedList << configString( list.first() );
    } else {
      const QModelIndex idx = EntityTreeModel::modelIndexForCollection( this, col );
      if ( !idx.isValid() )
        continue;

      if ( !containsMove && idx.data( EntityTreeModel::ParentCollectionRole ).value<Collection>().id() != parentCol.id() )
        containsMove = true;

      droppedList << configString( idx );
    }
  }

  QStringList existingList = d->m_orderConfig.readEntry( QString::number( parentCol.id() ), QStringList() );

  for ( int i = 0; i < droppedList.size(); ++i )
  {
    const QString droppedItem = droppedList.at( i );
    const int existingIndex = existingList.indexOf( droppedItem );
    existingList.removeAt( existingIndex );
    existingList.insert( row + i - (existingIndex > row ? 0 : 1), droppedList.at( i ) );
  }

  d->m_orderConfig.writeEntry( QString::number( parentCol.id() ), existingList );

  if ( containsMove )
  {
    bool result = QSortFilterProxyModel::dropMimeData( data, action, row, column, parent );
    invalidate();
    return result;
  }
  invalidate();
  return true;
}

void EntityOrderProxyModelPrivate::saveOrder( const QModelIndex &parent )
{
  Q_Q( const EntityOrderProxyModel );
  int rowCount = q->rowCount( parent );

  if ( rowCount == 0 )
    return;

  static const int column = 0;
  QModelIndex childIndex = q->index( 0, column, parent );

  QString parentKey = q->parentConfigString( childIndex );

  if ( parentKey.isEmpty() )
    return;

  QStringList list;

  list << q->configString( childIndex );
  saveOrder( childIndex );

  for ( int row = 1; row < rowCount; ++row )
  {
    childIndex = q->index( row, column, parent );
    list << q->configString( childIndex );
    saveOrder( childIndex );
  }

  m_orderConfig.writeEntry( parentKey, list );
}

QString EntityOrderProxyModel::parentConfigString( const QModelIndex& index ) const
{
  return QString::number( index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>().id() );
}

QString EntityOrderProxyModel::configString( const QModelIndex& index ) const
{
  Entity::Id eId = index.data( EntityTreeModel::ItemIdRole ).toLongLong();
  if ( eId != -1 )
  {
    return QString::fromLatin1( "i" ) + QString::number( eId );
  }
  eId = index.data( EntityTreeModel::CollectionIdRole ).toLongLong();
  if ( eId != -1 )
  {
    return QString::fromLatin1( "c" ) + QString::number( eId );
  }
  Q_ASSERT( !"Invalid entity" );
  return QString();
}

void EntityOrderProxyModel::saveOrder()
{
  Q_D( EntityOrderProxyModel );
  d->saveOrder( QModelIndex() );
  d->m_orderConfig.sync();
}

void EntityOrderProxyModel::clearOrder( const QModelIndex& parent )
{
  Q_D( EntityOrderProxyModel );

  QString parentKey = parentConfigString( index( 0, 0, parent ) );

  if ( parentKey.isEmpty() )
    return;

  d->m_orderConfig.deleteEntry( parentKey );
  invalidate();
}

void EntityOrderProxyModel::clearTreeOrder()
{
  Q_D( EntityOrderProxyModel );
  d->m_orderConfig.deleteGroup();
  invalidate();
}
