/*
    Copyright (c) 2010 Tobias Koenig <tokoe@kde.org>

    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 "actionstatemanager_p.h"

#include "agentmanager.h"
#include "collectionutils_p.h"
#include "pastehelper_p.h"
#include "specialcollectionattribute_p.h"
#include "standardactionmanager.h"

#include <QtGui/QApplication>
#include <QtGui/QClipboard>

using namespace Akonadi;

static bool canCreateSubCollection( const Collection &collection )
{
  if ( !( collection.rights() & Collection::CanCreateCollection ) )
    return false;

  if ( !collection.contentMimeTypes().contains( Collection::mimeType() ) )
    return false;

  return true;
}

static inline bool canContainItems( const Collection &collection )
{
  if ( collection.contentMimeTypes().isEmpty() )
    return false;

  if ( (collection.contentMimeTypes().count() == 1) &&
       (collection.contentMimeTypes().first() == Collection::mimeType()) )
    return false;

  return true;
}

ActionStateManager::ActionStateManager()
  : mReceiver( 0 )
{
}

ActionStateManager::~ActionStateManager()
{
}

void ActionStateManager::setReceiver( QObject *object )
{
  mReceiver = object;
}

void ActionStateManager::updateState( const Collection::List &collections, const Item::List &items )
{
  const bool singleCollectionSelected = (collections.count() == 1);
  const bool multipleCollectionsSelected = (collections.count() > 1);
  const bool atLeastOneCollectionSelected = (singleCollectionSelected || multipleCollectionsSelected);
  const int collectionCount = collections.count();

  const bool singleItemSelected = (items.count() == 1);
  const bool multipleItemsSelected = (items.count() > 1);
  const bool atLeastOneItemSelected = (singleItemSelected || multipleItemsSelected);
  const int itemCount = items.count();

  bool canDeleteCollections = (collections.count() > 0);
  foreach ( const Collection &collection, collections ) {
    // do we have the necessary rights?
    if ( !(collection.rights() & Collection::CanDeleteCollection) )
      canDeleteCollections = false;

    if ( isRootCollection( collection ) )
      canDeleteCollections = false;

    if ( isResourceCollection( collection ) )
      canDeleteCollections = false;
  }

  bool canCutCollections = canDeleteCollections; // we must be able to delete for cutting
  foreach ( const Collection &collection, collections ) {
    if ( isSpecialCollection( collection ) )
      canCutCollections = false;

    if ( !isFolderCollection( collection ) )
      canCutCollections = false;
  }

  const bool canMoveCollections = canCutCollections; // we must be able to cut for moving

  bool canCopyCollections = (collections.count() > 0);
  foreach ( const Collection &collection, collections ) {
    if ( isRootCollection( collection ) )
      canCopyCollections = false;

    if ( !isFolderCollection( collection ) )
      canCopyCollections = false;
  }

  bool canAddToFavoriteCollections = (collections.count() > 0);
  foreach ( const Collection &collection, collections ) {
    if ( isRootCollection( collection ) )
      canAddToFavoriteCollections = false;

    if ( isFavoriteCollection( collection ) )
      canAddToFavoriteCollections = false;

    if ( !isFolderCollection( collection ) )
      canAddToFavoriteCollections = false;

    if ( !canContainItems( collection ) )
      canAddToFavoriteCollections = false;
  }

  bool canRemoveFromFavoriteCollections = (collections.count() > 0);
  foreach ( const Collection &collection, collections ) {
    if ( !isFavoriteCollection( collection ) )
      canRemoveFromFavoriteCollections = false;
  }

  bool collectionsAreFolders = (!collections.isEmpty() ? true : false);

  foreach ( Collection collection, collections ) {
    if ( !isFolderCollection( collection ) ) {
      collectionsAreFolders = false;
      break;
    }
  }

  const Collection collection = (!collections.isEmpty() ? collections.first() : Collection());

  // collection specific actions
  enableAction( StandardActionManager::CreateCollection, singleCollectionSelected && // we can create only inside one collection
                                                         canCreateSubCollection( collection ) ); // we need the necessary rights

  enableAction( StandardActionManager::DeleteCollections, canDeleteCollections );

  enableAction( StandardActionManager::CopyCollections, canCopyCollections );

  enableAction( StandardActionManager::CutCollections, canCutCollections );

  enableAction( StandardActionManager::CopyCollectionToMenu, canCopyCollections );

  enableAction( StandardActionManager::MoveCollectionToMenu, canMoveCollections );

  enableAction( StandardActionManager::CopyCollectionToDialog, canCopyCollections );

  enableAction( StandardActionManager::MoveCollectionToDialog, canMoveCollections );

  enableAction( StandardActionManager::CollectionProperties, singleCollectionSelected && // we can only configure one collection at a time
                                                             !isRootCollection( collection ) ); // we can not configure the root collection

  enableAction( StandardActionManager::SynchronizeCollections, atLeastOneCollectionSelected && 
                                                               collectionsAreFolders ); // it must be a valid folder collection

  enableAction( StandardActionManager::SynchronizeCollectionsRecursive, atLeastOneCollectionSelected &&
                                                               collectionsAreFolders ); // it must be a valid folder collection
#ifndef QT_NO_CLIPBOARD
  enableAction( StandardActionManager::Paste, singleCollectionSelected && // we can paste only into a single collection
                                              PasteHelper::canPaste( QApplication::clipboard()->mimeData(), collection ) ); // there must be data on the clipboard
#else
  enableAction( StandardActionManager::Paste, false ); // no support for clipboard -> no paste
#endif

  // favorite collections specific actions
  enableAction( StandardActionManager::AddToFavoriteCollections, canAddToFavoriteCollections );

  enableAction( StandardActionManager::RemoveFromFavoriteCollections, canRemoveFromFavoriteCollections );

  enableAction( StandardActionManager::RenameFavoriteCollection, singleCollectionSelected && // we can rename only one collection at a time
                                                                 isFavoriteCollection( collection ) ); // it must be a favorite collection already

  // resource specific actions
  int resourceCollectionCount = 0;
  bool canDeleteResources = true;
  bool canConfigureResource = true;
  bool canSynchronizeResources = true;
  foreach ( const Collection &collection, collections ) {
    if ( isResourceCollection( collection ) ) {
      resourceCollectionCount++;

      // check that the 'NoConfig' flag is not set for the resource
      if ( hasResourceCapability( collection, QLatin1String( "NoConfig" ) ) )
        canConfigureResource = false;

    } else {
      // we selected a non-resource collection
      canDeleteResources = false;
      canConfigureResource = false;
      canSynchronizeResources = false;
    }
  }

  if ( resourceCollectionCount == 0 ) {
    // not a single resource collection has been selected
    canDeleteResources = false;
    canConfigureResource = false;
    canSynchronizeResources = false;
  }

  enableAction( StandardActionManager::CreateResource, true );
  enableAction( StandardActionManager::DeleteResources, canDeleteResources );
  enableAction( StandardActionManager::ResourceProperties, canConfigureResource );
  enableAction( StandardActionManager::SynchronizeResources, canSynchronizeResources );

  // item specific actions
  bool canDeleteItems = (items.count() > 0); //TODO: fixme
  foreach ( const Item &item, items ) {
    const Collection parentCollection = item.parentCollection();
    if ( !parentCollection.isValid() )
      continue;

    canDeleteItems = canDeleteItems && (parentCollection.rights() & Collection::CanDeleteItem);
  }

  enableAction( StandardActionManager::CopyItems, atLeastOneItemSelected ); // we need items to work with

  enableAction( StandardActionManager::CutItems, atLeastOneItemSelected && // we need items to work with
                                                 canDeleteItems ); // we need the necessary rights

  enableAction( StandardActionManager::DeleteItems, atLeastOneItemSelected && // we need items to work with
                                                    canDeleteItems ); // we need the necessary rights

  enableAction( StandardActionManager::CopyItemToMenu, atLeastOneItemSelected ); // we need items to work with

  enableAction( StandardActionManager::MoveItemToMenu, atLeastOneItemSelected && // we need items to work with
                                                       canDeleteItems ); // we need the necessary rights

  enableAction( StandardActionManager::CopyItemToDialog, atLeastOneItemSelected ); // we need items to work with

  enableAction( StandardActionManager::MoveItemToDialog, atLeastOneItemSelected && // we need items to work with
                                                       canDeleteItems ); // we need the necessary rights

  // update the texts of the actions
  updatePluralLabel( StandardActionManager::CopyCollections, collectionCount );
  updatePluralLabel( StandardActionManager::CopyItems, itemCount );
  updatePluralLabel( StandardActionManager::DeleteItems, itemCount );
  updatePluralLabel( StandardActionManager::CutItems, itemCount );
  updatePluralLabel( StandardActionManager::CutCollections, collectionCount );
  updatePluralLabel( StandardActionManager::DeleteCollections, collectionCount );
  updatePluralLabel( StandardActionManager::SynchronizeCollections, collectionCount );
  updatePluralLabel( StandardActionManager::SynchronizeCollectionsRecursive, collectionCount );
  updatePluralLabel( StandardActionManager::DeleteResources, resourceCollectionCount );
  updatePluralLabel( StandardActionManager::SynchronizeResources, resourceCollectionCount );
}

bool ActionStateManager::isRootCollection( const Collection &collection ) const
{
  return CollectionUtils::isRoot( collection );
}

bool ActionStateManager::isResourceCollection( const Collection &collection ) const
{
  return CollectionUtils::isResource( collection );
}

bool ActionStateManager::isFolderCollection( const Collection &collection ) const
{
  return (CollectionUtils::isFolder( collection ) ||
          CollectionUtils::isResource( collection ) ||
          CollectionUtils::isStructural( collection ));
}

bool ActionStateManager::isSpecialCollection( const Collection &collection ) const
{
  return collection.hasAttribute<SpecialCollectionAttribute>();
}

bool ActionStateManager::isFavoriteCollection( const Collection &collection ) const
{
  if ( !mReceiver )
    return false;

  bool result = false;
  QMetaObject::invokeMethod( mReceiver, "isFavoriteCollection", Qt::DirectConnection,
                             Q_RETURN_ARG( bool, result ), Q_ARG( Akonadi::Collection, collection ) );

  return result;
}

bool ActionStateManager::hasResourceCapability( const Collection &collection, const QString &capability ) const
{
  const Akonadi::AgentInstance instance = AgentManager::self()->instance( collection.resource() );

  return instance.type().capabilities().contains( capability );
}

void ActionStateManager::enableAction( int action, bool state )
{
  if ( !mReceiver )
    return;

  QMetaObject::invokeMethod( mReceiver, "enableAction", Qt::DirectConnection, Q_ARG( int, action ), Q_ARG( bool, state ) );
}

void ActionStateManager::updatePluralLabel( int action, int count )
{
  if ( !mReceiver )
    return;

  QMetaObject::invokeMethod( mReceiver, "updatePluralLabel", Qt::DirectConnection, Q_ARG( int, action ), Q_ARG( int, count ) );
}
