/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *   Copyright (c) 2007 Volker Krause <vkrause@kde.org>                    *
 *                                                                         *
 *   This program 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 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 Library 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.         *
 ***************************************************************************/

#include "agentmanager.h"

#include "agentmanageradaptor.h"
#include "agentmanagerinternaladaptor.h"
#include "agentprocessinstance.h"
#include "agentserverinterface.h"
#include "agentthreadinstance.h"
#include "akdebug.h"
#include "libs/protocol_p.h"
#include "libs/xdgbasedirs_p.h"
#include "preprocessor_manager.h"
#include "processcontrol.h"
#include "resource_manager.h"
#include "serverinterface.h"

#include <akapplication.h>
#include <akdbus.h>
#include <akstandarddirs.h>

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#ifndef QT_NO_DEBUG
#include <QtCore/QFileSystemWatcher>
#endif
#include <QtCore/QSettings>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusError>

#include <boost/scoped_ptr.hpp>

using Akonadi::ProcessControl;

#if defined(Q_WS_MAEMO_5) || defined(_WIN32_WCE)
static bool enableAgentServerDefault = true;
#else
static bool enableAgentServerDefault = false;
#endif

AgentManager::AgentManager( QObject *parent )
  : QObject( parent ),
    mAgentServer( 0 )
#ifndef QT_NO_DEBUG
  , mAgentWatcher( new QFileSystemWatcher( this ) )
#endif
{
  new AgentManagerAdaptor( this );
  new AgentManagerInternalAdaptor( this );
  QDBusConnection::sessionBus().registerObject( QLatin1String("/AgentManager"), this );

  connect( QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)),
           this, SLOT(serviceOwnerChanged(QString,QString,QString)) );

  if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AkDBus::serviceName(AkDBus::Server) ) )
    akFatal() << "akonadiserver already running!";

  const QSettings settings( AkStandardDirs::agentConfigFile( Akonadi::XdgBaseDirs::ReadOnly ), QSettings::IniFormat );
  mAgentServerEnabled = settings.value( QLatin1String("AgentServer/Enabled"), enableAgentServerDefault ).toBool();

  QStringList serviceArgs;
  if ( !AkApplication::instanceIdentifier().isEmpty() )
    serviceArgs << QLatin1String("--instance") << AkApplication::instanceIdentifier();

  mStorageController = new Akonadi::ProcessControl;
  connect( mStorageController, SIGNAL(unableToStart()), SLOT(serverFailure()) );
  mStorageController->start( QLatin1String("akonadiserver"), serviceArgs, Akonadi::ProcessControl::RestartOnCrash );

  if ( mAgentServerEnabled ) {
    mAgentServer = new Akonadi::ProcessControl;
    connect( mAgentServer, SIGNAL(unableToStart()), SLOT(agentServerFailure()) );
    mAgentServer->start( QLatin1String("akonadi_agent_server"), serviceArgs, Akonadi::ProcessControl::RestartOnCrash );
  }

#ifndef QT_NO_DEBUG
  connect( mAgentWatcher, SIGNAL(fileChanged(QString)), SLOT(agentExeChanged(QString)) );
#endif
}

void AgentManager::continueStartup()
{
  // prevent multiple calls in case the server has to be restarted
  static bool first = true;
  if ( !first )
    return;

  first = false;

  readPluginInfos();
  foreach ( const AgentType &info, mAgents )
    emit agentTypeAdded( info.identifier );

  const QStringList pathList = pluginInfoPathList();

#ifndef QT_NO_DEBUG
  foreach ( const QString &path, pathList ) {
    QFileSystemWatcher *watcher = new QFileSystemWatcher( this );
    watcher->addPath( path );

    connect( watcher, SIGNAL(directoryChanged(QString)),
             this, SLOT(updatePluginInfos()) );
  }
#endif

  load();
  foreach ( const AgentType &info, mAgents )
    ensureAutoStart( info );

  // register the real service name once everything is up an running
  if ( !QDBusConnection::sessionBus().registerService( AkDBus::serviceName(AkDBus::Control) ) ) {
    // besides a race with an older Akonadi server I have no idea how we could possibly get here...
    akFatal() << "Unable to register service as" << AkDBus::serviceName(AkDBus::Control)
              << "despite having the lock. Error was:" << QDBusConnection::sessionBus().lastError().message();
  }
  akDebug() << "Akonadi server is now operational.";
}

AgentManager::~AgentManager()
{
  cleanup();
}

void AgentManager::cleanup()
{
  foreach ( const AgentInstance::Ptr &instance, mAgentInstances )
    instance->quit();

  mAgentInstances.clear();

  mStorageController->setCrashPolicy( ProcessControl::StopOnCrash );
  org::freedesktop::Akonadi::Server *serverIface =
    new org::freedesktop::Akonadi::Server( AkDBus::serviceName(AkDBus::Server), QLatin1String("/Server"),
                                           QDBusConnection::sessionBus(), this );
  serverIface->quit();

  if ( mAgentServer ) {
    mAgentServer->setCrashPolicy( ProcessControl::StopOnCrash );
    org::freedesktop::Akonadi::AgentServer *agentServerIface =
        new org::freedesktop::Akonadi::AgentServer( AkDBus::serviceName(AkDBus::AgentServer),
                                                    QLatin1String("/AgentServer"), QDBusConnection::sessionBus(), this );
    agentServerIface->quit();
  }

  delete mStorageController;
  mStorageController = 0;

  delete mAgentServer;
  mAgentServer = 0;
}

QStringList AgentManager::agentTypes() const
{
  return mAgents.keys();
}

QString AgentManager::agentName( const QString &identifier, const QString &language ) const
{
  if ( !checkAgentExists( identifier ) )
    return QString();

  const QString name = mAgents.value( identifier ).name.value( language );
  return name.isEmpty() ? mAgents.value( identifier ).name.value( QLatin1String("en_US") ) : name;
}

QString AgentManager::agentComment( const QString &identifier, const QString &language ) const
{
  if ( !checkAgentExists( identifier ) )
    return QString();

  const QString comment = mAgents.value( identifier ).comment.value( language );
  return comment.isEmpty() ? mAgents.value( identifier ).comment.value( QLatin1String("en_US") ) : comment;
}

QString AgentManager::agentIcon( const QString &identifier ) const
{
  if ( !checkAgentExists( identifier ) )
    return QString();

  const AgentType info = mAgents.value( identifier );
  if ( !info.icon.isEmpty() )
    return info.icon;

  return QLatin1String("application-x-executable");
}

QStringList AgentManager::agentMimeTypes( const QString &identifier ) const
{
  if ( !checkAgentExists( identifier ) )
    return QStringList();

  return mAgents.value( identifier ).mimeTypes;
}

QStringList AgentManager::agentCapabilities( const QString &identifier ) const
{
  if ( !checkAgentExists( identifier ) )
    return QStringList();
  return mAgents.value( identifier ).capabilities;
}

AgentInstance::Ptr AgentManager::createAgentInstance( const AgentType &info )
{
  switch ( info.launchMethod ) {
    case AgentType::Server:
      return AgentInstance::Ptr( new Akonadi::AgentThreadInstance( this ) );
    case AgentType::Launcher: // Fall through
    case AgentType::Process:
      return AgentInstance::Ptr( new Akonadi::AgentProcessInstance( this ) );
    default:
      Q_ASSERT_X( false, "AgentManger::createAgentInstance", "Unhandled AgentType::LaunchMethod case" );
  }

  return AgentInstance::Ptr();
}

QString AgentManager::createAgentInstance( const QString &identifier )
{
  if ( !checkAgentExists( identifier ) )
    return QString();

  const AgentType agentInfo = mAgents.value( identifier );
  mAgents[ identifier ].instanceCounter++;

  const AgentInstance::Ptr instance = createAgentInstance( agentInfo );
  if ( agentInfo.capabilities.contains( AgentType::CapabilityUnique ) )
    instance->setIdentifier( identifier );
  else
    instance->setIdentifier( QString::fromLatin1( "%1_%2" ).arg( identifier, QString::number( agentInfo.instanceCounter ) ) );

  if ( mAgentInstances.contains( instance->identifier() ) ) {
    akError() << Q_FUNC_INFO << "Cannot create another instance of agent" << identifier;
    return QString();
  }

  // Return from this dbus call before we do the next. Otherwise dbus brakes for
  // this process.
  if ( calledFromDBus() )
    connection().send( message().createReply( instance->identifier() ) );

  if ( !instance->start( agentInfo ) )
    return QString();

  mAgentInstances.insert( instance->identifier(), instance );
  registerAgentAtServer( instance->identifier(), agentInfo );
  save();

  return instance->identifier();
}

void AgentManager::removeAgentInstance( const QString &identifier )
{
  if ( !mAgentInstances.contains( identifier ) ) {
    akError() << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist";
    return;
  }

  const AgentInstance::Ptr instance = mAgentInstances.value( identifier );
  if ( instance->hasAgentInterface() ) {
    instance->cleanup();
  } else {
    akError() << Q_FUNC_INFO << "Agent instance" << identifier << "has no interface!";
  }

  mAgentInstances.remove( identifier );

  save();

  org::freedesktop::Akonadi::ResourceManager resmanager( AkDBus::serviceName(AkDBus::Server), QLatin1String( "/ResourceManager" ), QDBusConnection::sessionBus(), this );
  resmanager.removeResourceInstance( instance->identifier() );

  // Kill the preprocessor instance, if any.
  org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(
      AkDBus::serviceName(AkDBus::Server),
      QLatin1String( "/PreprocessorManager" ),
      QDBusConnection::sessionBus(),
      this
    );

  preProcessorManager.unregisterInstance( instance->identifier() );

  if ( instance->hasAgentInterface() ) {
    qDebug() << "AgentManager::removeAgentInstance: calling instance->quit()";
    instance->quit();
  } else {
    akError() << Q_FUNC_INFO << "Agent instance" << identifier << "has no interface!";
  }

  emit agentInstanceRemoved( identifier );
}

QString AgentManager::agentInstanceType( const QString &identifier )
{
  if ( !mAgentInstances.contains( identifier ) ) {
    akError() << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist";
    return QString();
  }

  return mAgentInstances.value( identifier )->agentType();
}

QStringList AgentManager::agentInstances() const
{
  return mAgentInstances.keys();
}

int AgentManager::agentInstanceStatus( const QString &identifier ) const
{
  if ( !checkInstance( identifier ) )
    return 2;

  return mAgentInstances.value( identifier )->status();
}

QString AgentManager::agentInstanceStatusMessage( const QString &identifier ) const
{
  if ( !checkInstance( identifier ) )
    return QString();

  return mAgentInstances.value( identifier )->statusMessage();
}

uint AgentManager::agentInstanceProgress( const QString &identifier ) const
{
  if ( !checkInstance( identifier ) )
    return 0;

  return mAgentInstances.value( identifier )->progress();
}

QString AgentManager::agentInstanceProgressMessage( const QString &identifier ) const
{
  Q_UNUSED( identifier );

  return QString();
}

void AgentManager::agentInstanceConfigure( const QString &identifier, qlonglong windowId )
{
  if ( !checkAgentInterfaces( identifier, QLatin1String("agentInstanceConfigure") ) )
    return;

  mAgentInstances.value( identifier )->configure( windowId );
}

bool AgentManager::agentInstanceOnline(const QString & identifier)
{
  if ( !checkInstance( identifier ) )
    return false;

  return mAgentInstances.value( identifier )->isOnline();
}

void AgentManager::setAgentInstanceOnline( const QString &identifier, bool state )
{
  if ( !checkAgentInterfaces( identifier, QLatin1String( "setAgentInstanceOnline" ) ) )
    return;

  mAgentInstances.value( identifier )->statusInterface()->setOnline( state );
}

// resource specific methods //
void AgentManager::setAgentInstanceName( const QString &identifier, const QString &name )
{
  if ( !checkResourceInterface( identifier, QLatin1String( "setAgentInstanceName" ) ) )
    return;

  mAgentInstances.value( identifier )->resourceInterface()->setName( name );
}

QString AgentManager::agentInstanceName( const QString &identifier, const QString &language ) const
{
  if ( !checkInstance( identifier ) )
    return QString();

  const AgentInstance::Ptr instance = mAgentInstances.value( identifier );
  if ( !instance->resourceName().isEmpty() )
    return instance->resourceName();

  if ( !checkAgentExists( instance->agentType() ) )
    return QString();

  const QString name = mAgents.value( instance->agentType() ).name.value( language );
  return name.isEmpty() ? mAgents.value( instance->agentType() ).name.value( QLatin1String("en_US") ) : name;
}

void AgentManager::agentInstanceSynchronize( const QString &identifier )
{
  if ( !checkResourceInterface( identifier, QLatin1String( "agentInstanceSynchronize" ) ) )
    return;

  mAgentInstances.value( identifier )->resourceInterface()->synchronize();
}

void AgentManager::agentInstanceSynchronizeCollectionTree( const QString &identifier )
{
  if ( !checkResourceInterface( identifier, QLatin1String( "agentInstanceSynchronizeCollectionTree" ) ) )
    return;

  mAgentInstances.value( identifier  )->resourceInterface()->synchronizeCollectionTree();
}

void AgentManager::agentInstanceSynchronizeCollection( const QString &identifier, qint64 collection )
{
  agentInstanceSynchronizeCollection( identifier, collection, false );
}

void AgentManager::agentInstanceSynchronizeCollection( const QString &identifier, qint64 collection, bool recursive )
{
  if ( !checkResourceInterface( identifier, QLatin1String( "agentInstanceSynchronizeCollection" ) ) )
    return;

  mAgentInstances.value( identifier )->resourceInterface()->synchronizeCollection( collection, recursive );
}

void AgentManager::restartAgentInstance( const QString &identifier )
{
  if ( !checkInstance( identifier ) )
    return;

  mAgentInstances.value( identifier )->restartWhenIdle();
}

void AgentManager::updatePluginInfos()
{
  const QHash<QString, AgentType> oldInfos = mAgents;
  readPluginInfos();

  foreach ( const AgentType &oldInfo, oldInfos ) {
    if ( !mAgents.contains( oldInfo.identifier ) )
      emit agentTypeRemoved( oldInfo.identifier );
  }

  foreach ( const AgentType &newInfo, mAgents ) {
    if ( !oldInfos.contains( newInfo.identifier ) ) {
      emit agentTypeAdded( newInfo.identifier );
      ensureAutoStart( newInfo );
    }
  }
}

void AgentManager::readPluginInfos()
{
#ifndef QT_NO_DEBUG
  if ( !mAgentWatcher->files().isEmpty() )
    mAgentWatcher->removePaths( mAgentWatcher->files() );
#endif
  mAgents.clear();

  const QStringList pathList = pluginInfoPathList();

  foreach ( const QString &path, pathList ) {
      const QDir directory( path, QLatin1String("*.desktop") );
      readPluginInfos( directory );
  }
}

void AgentManager::readPluginInfos( const QDir &directory )
{
  const QStringList files = directory.entryList();
  qDebug() << "PLUGINS: " << directory.canonicalPath();
  qDebug() << "PLUGINS: " << files;
  for ( int i = 0; i < files.count(); ++i ) {
    const QString fileName = directory.absoluteFilePath( files[ i ] );

    AgentType agentInfo;
    if ( agentInfo.load( fileName, this ) ) {
      if ( mAgents.contains( agentInfo.identifier ) ) {
        akError() << Q_FUNC_INFO << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName;
        continue;
      }

      const QString disableAutostart = getEnv( "AKONADI_DISABLE_AGENT_AUTOSTART" );
      if ( !disableAutostart.isEmpty() ) {
        qDebug() << "Autostarting of agents is disabled.";
        agentInfo.capabilities.removeOne( AgentType::CapabilityAutostart );
      }

      if ( !mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server )
        agentInfo.launchMethod = AgentType::Launcher;

      if ( agentInfo.launchMethod == AgentType::Process ) {
        const QString executable = Akonadi::XdgBaseDirs::findExecutableFile( agentInfo.exec );
        if ( executable.isEmpty() ) {
          akError() << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!";
          continue;
        }
#ifndef QT_NO_DEBUG
        if ( !mAgentWatcher->files().contains( executable ) )
          mAgentWatcher->addPath( executable );
#endif
      }

      qDebug() << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities;
      mAgents.insert( agentInfo.identifier, agentInfo );
    }
  }
}

QStringList AgentManager::pluginInfoPathList()
{
  return Akonadi::XdgBaseDirs::findAllResourceDirs( "data", QLatin1String( "akonadi/agents" ) );
}

void AgentManager::load()
{
  org::freedesktop::Akonadi::ResourceManager resmanager( AkDBus::serviceName(AkDBus::Server), QLatin1String( "/ResourceManager" ), QDBusConnection::sessionBus(), this );
  const QStringList knownResources = resmanager.resourceInstances();

  QSettings file( AkStandardDirs::agentConfigFile( Akonadi::XdgBaseDirs::ReadOnly ), QSettings::IniFormat );
  file.beginGroup( QLatin1String("Instances") );
  const QStringList entries = file.childGroups();
  for ( int i = 0; i < entries.count(); ++i ) {
    const QString instanceIdentifier = entries[ i ];

    if ( mAgentInstances.contains( instanceIdentifier ) ) {
      akError() << Q_FUNC_INFO << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc";
      continue;
    }

    file.beginGroup( entries[ i ] );

    const QString agentType = file.value( QLatin1String("AgentType") ).toString();
    if ( !mAgents.contains( agentType ) ) {
      akError() << Q_FUNC_INFO << "Reference to unknown agent type" << agentType << "in agentsrc";
      file.endGroup();
      continue;
    }
    const AgentType type = mAgents.value( agentType );

    // recover if the db has been deleted in the meantime or got otherwise corrupted
    if ( !knownResources.contains( instanceIdentifier ) && type.capabilities.contains( AgentType::CapabilityResource ) ) {
      akDebug() << "Recovering instance" << instanceIdentifier << "after database loss";
      registerAgentAtServer( instanceIdentifier, type );
    }

    const AgentInstance::Ptr instance = createAgentInstance( type );
    instance->setIdentifier( instanceIdentifier );
    if ( instance->start( type ) )
      mAgentInstances.insert( instanceIdentifier, instance );

    file.endGroup();
  }

  file.endGroup();
}

void AgentManager::save()
{
  QSettings file( AkStandardDirs::agentConfigFile( Akonadi::XdgBaseDirs::WriteOnly ), QSettings::IniFormat );

  foreach ( const AgentType &info, mAgents )
    info.save( &file );

  file.beginGroup( QLatin1String("Instances") );
  file.remove( QString() );
  foreach ( const AgentInstance::Ptr &instance, mAgentInstances ) {
    file.beginGroup( instance->identifier() );
    file.setValue( QLatin1String("AgentType"), instance->agentType() );
    file.endGroup();
  }

  file.endGroup();

  file.sync();
}

void AgentManager::serviceOwnerChanged( const QString &name, const QString&, const QString &newOwner )
{
  // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason
  // and this is where we "hook up" our different Agent interfaces.

  //qDebug() << "Service " << name << " owner changed from " << oldOwner << " to " << newOwner;

  if ( (name == AkDBus::serviceName(AkDBus::Server) || name == AkDBus::serviceName(AkDBus::AgentServer)) && !newOwner.isEmpty() ) {
    if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AkDBus::serviceName(AkDBus::Server) )
      && ( !mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered( AkDBus::serviceName(AkDBus::AgentServer) ) ) )
    {
      // server is operational, start agents
      continueStartup();
    }
  }

  AkDBus::AgentType agentType = AkDBus::Unknown;
  const QString agentIdentifier = AkDBus::parseAgentServiceName( name, agentType );
  switch (agentType) {
    case AkDBus::Agent:
    {
      // An agent service went up or down
      if ( newOwner.isEmpty() )
        return; // It went down: we don't care here.

      if ( !mAgentInstances.contains( agentIdentifier ) )
        return;

      const AgentInstance::Ptr instance = mAgentInstances.value( agentIdentifier );
      const bool restarting = instance->hasAgentInterface();
      if ( !instance->obtainAgentInterface() )
        return;

      if ( !restarting )
        emit agentInstanceAdded( agentIdentifier );

      break;
    }
    case AkDBus::Resource:
    {
      // A resource service went up or down
      if ( newOwner.isEmpty() )
        return; // It went down: we don't care here.

      if ( !mAgentInstances.contains( agentIdentifier ) )
        return;

      mAgentInstances.value( agentIdentifier )->obtainResourceInterface();

      break;
    }
    case AkDBus::Preprocessor:
    {
      // A preprocessor service went up or down

      // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface
      // should be already up (as it's registered before the preprocessor one).
      // So if we don't know about the preprocessor as agent instance
      // then it's not our preprocessor.

      // If the preprocessor is going down then either the agent interface already
      // went down (and it has been already unregistered on the manager side)
      // or it's still registered as agent and WE have to unregister it.
      // The order of interface deletions depends on Qt but we handle both cases.

      // Check if we "know" about it.
      qDebug() << "Preprocessor " << agentIdentifier << " is going up or down...";

      if ( !mAgentInstances.contains( agentIdentifier ) ) {
        qDebug() << "But it isn't registered as agent... not mine (anymore?)";
        return; // not our agent (?)
      }

      org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(
          AkDBus::serviceName(AkDBus::Server),
          QLatin1String( "/PreprocessorManager" ),
          QDBusConnection::sessionBus(),
          this
        );

      if ( !preProcessorManager.isValid() ) {
        akError() << Q_FUNC_INFO <<"Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError();
      } else {
        if ( newOwner.isEmpty() ) {
          // The preprocessor went down. Unregister it on server side.

          preProcessorManager.unregisterInstance( agentIdentifier );

        } else {

          // The preprocessor went up. Register it on server side.

          if ( !mAgentInstances.value( agentIdentifier )->obtainPreprocessorInterface() ) {
            // Hm.. couldn't hook up its preprocessor interface..
            // Make sure we don't have it in the preprocessor chain
            qWarning() << "Couldn't obtain preprocessor interface for instance " << agentIdentifier;

            preProcessorManager.unregisterInstance( agentIdentifier );
            return;
          }

          qDebug() << "Registering preprocessor instance " << agentIdentifier;

          // Add to the preprocessor chain
          preProcessorManager.registerInstance( agentIdentifier );
        }
      }

      break;
    }
    default: break;
  }
}

bool AgentManager::checkInstance( const QString &identifier ) const
{
  if ( !mAgentInstances.contains( identifier ) ) {
    qWarning() << "Agent instance with identifier " << identifier << " does not exist";
    return false;
  }

  return true;
}

bool AgentManager::checkResourceInterface( const QString &identifier, const QString &method ) const
{
  if ( !checkInstance( identifier ) )
    return false;

  if ( !mAgents[ mAgentInstances[ identifier ]->agentType() ].capabilities.contains( QLatin1String("Resource") ) )
    return false;

  if ( !mAgentInstances[ identifier ]->hasResourceInterface() ) {
    qWarning() << QLatin1String( "AgentManager::" ) + method << " Agent instance "
        << identifier << " has no resource interface!";
    return false;
  }

  return true;
}

bool AgentManager::checkAgentExists( const QString &identifier ) const
{
  if ( !mAgents.contains( identifier ) ) {
    qWarning() << "Agent instance " << identifier << " does not exist.";
    return false;
  }

  return true;
}

bool AgentManager::checkAgentInterfaces( const QString &identifier, const QString &method ) const
{
  if ( !checkInstance( identifier ) )
    return false;

  if ( !mAgentInstances.value( identifier )->hasAgentInterface() ) {
    qWarning() << "Agent instance (" << method << ") " << identifier << " has no agent interface.";
    return false;
  }

  return true;
}

void AgentManager::ensureAutoStart( const AgentType &info )
{
  if ( !info.capabilities.contains( AgentType::CapabilityAutostart ) )
    return; // no an autostart agent

  org::freedesktop::Akonadi::AgentServer agentServer( AkDBus::serviceName(AkDBus::AgentServer),
                                                      QLatin1String("/AgentServer"), QDBusConnection::sessionBus(), this );

  if ( mAgentInstances.contains( info.identifier ) ||
       (agentServer.isValid() && agentServer.started( info.identifier )) ) {
    return; // already running
  }

  const AgentInstance::Ptr instance = createAgentInstance( info );
  instance->setIdentifier( info.identifier );
  if ( instance->start( info ) ) {
    mAgentInstances.insert( instance->identifier(), instance );
    registerAgentAtServer( instance->identifier(), info );
    save();
  }
}

void AgentManager::agentExeChanged( const QString &fileName )
{
  if ( !QFile::exists( fileName ) )
    return;

  foreach ( const AgentType &type, mAgents ) {
    if ( fileName.endsWith( type.exec ) ) {
      foreach ( const AgentInstance::Ptr &instance, mAgentInstances ) {
        if ( instance->agentType() == type.identifier )
          instance->restartWhenIdle();
      }
    }
  }
}

void AgentManager::registerAgentAtServer( const QString &agentIdentifier, const AgentType &type )
{
  if ( type.capabilities.contains( AgentType::CapabilityResource ) ) {
    boost::scoped_ptr<org::freedesktop::Akonadi::ResourceManager> resmanager(
      new org::freedesktop::Akonadi::ResourceManager( AkDBus::serviceName(AkDBus::Server),
                                                      QLatin1String( "/ResourceManager" ),
                                                      QDBusConnection::sessionBus(), this ) );
    resmanager->addResourceInstance( agentIdentifier, type.capabilities );
  }
}


void AgentManager::addSearch( const QString &query, const QString &queryLanguage, qint64 resultCollectionId )
{
  qDebug() << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId;
  foreach ( const AgentInstance::Ptr &instance, mAgentInstances ) {
    const AgentType type = mAgents.value( instance->agentType() );
    if ( type.capabilities.contains( AgentType::CapabilitySearch ) && instance->searchInterface() )
      instance->searchInterface()->addSearch( query, queryLanguage, resultCollectionId );
  }
}

void AgentManager::removeSearch( quint64 resultCollectionId )
{
  qDebug() << "AgentManager::removeSearch" << resultCollectionId;
  foreach ( const AgentInstance::Ptr &instance, mAgentInstances ) {
    const AgentType type = mAgents.value( instance->agentType() );
    if ( type.capabilities.contains( AgentType::CapabilitySearch ) && instance->searchInterface() )
      instance->searchInterface()->removeSearch( resultCollectionId );
  }
}

void AgentManager::agentServerFailure()
{
  qDebug() << "Failed to start AgentServer!";
  // if ( requiresAgentServer )
  //   QCoreApplication::instance()->exit( 255 );
}

void AgentManager::serverFailure()
{
  QCoreApplication::instance()->exit( 255 );
}

#include "agentmanager.moc"
