/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM 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 General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
#ifndef NMM_PLUGINMANAGER_HPP
#define NMM_PLUGINMANAGER_HPP

#include "nmm/nmm_prefix.hpp"

#include "IPluginManagerImpl.hpp"
#include "nmm/multimedia/IPluginManagerHandle.hpp"
#include "nmm/utils/NamedObject.hpp"

#include <map>
#include <list>
#include <string>

#include "visibility.hpp"

namespace NMM {
  class Registry;
  class NodeFactory;
  class SerializedException;
  class Plugin;
  class DynamicLibrary;

  /** The PluginManager loads libraries which contain NMM-Nodes that can be used in an application.
   * To find the plugins in a library, each library exports a global variable of name "nmm_plugins"
   * that is an array of plugins. This array is NULL-terminated. A plugin search and register nodes
   * that can be used on the system.
   */
  class DLLEXPORT PluginManager : public IPluginManagerImpl, public NamedObject, public IPluginManagerHandle {
    DECLARE_SINGLETON(PluginManager, CreateUsingNew, DefaultLifetime, MultiThreaded);
    friend class CreateUsingNew<PluginManager>;
  public:

    /** @name Local methods 
     * This methods can not be accessed through an interface, because they can only be used from local objects.
     */
    //@{
    /** Get array of plugins from a specific library. 
     * This method returns the array of plugins from a specific library. If the library is not load the
     * return value is NULL.
     * @param library The name of the library
     * @return array of plugins from a specific library, NULL if the library is not load
     */
    Plugin* requestPlugin(const string& library);

    /** Get array of plugins from a specific library. 
     * This method returns the array of plugins from a specific library. If the library is not load the
     * return value is NULL.
     * @param library The name of the library
     * @return array of plugins from a specific library, NULL if the library is not load
     */
    Plugin* const  requestPlugin(const string& library) const;

    /** Checks if plugin is load. 
     * This method returns true if the plugin is load, false otherwise.
     * @param library The name of the library
     * @sa loadLibrary
     * @return true if library is load, false otherwise
     */
    bool isPluginUsed(const string& library )          const;

    /** Checks if factory part is active. 
     * This method returns true if the factory part is active, so the nodes in a library can be
     * created by a NodeFactory.
     * @param library The name of the library
     * @return true if the factory part is active, false otherwise
     */
    bool isFactoryPartActive(const string& library )   const;

    /** Checks if registry part is active. 
     * This method returns true if the registry part is active, so the nodes in a library are managed
     * by a Registry.
     * @param library The name of the library
     * @return true if the registry part is active, false otherwise.
     */
    bool isRegistryPartActive(const string& library)   const;

    /** Adds list of ids to plugin.
     * @param plugin name of plugin
     * @param id_list list of ids
     */
    void defineObjectIDs(const string& plugin, const vector<list<ObjectID> >& id_list);

    /** Sets internal Registry pointer to NULL.
     *  Sets internal Registry pointer of plugins contained in loaded library to NULL, if it's equal to given registry pointer.
     *  @param registry pointer to registry
     */
    void removeRegistry(const Registry* registry);

    /** Sets internal NodeFactory pointer to NULL.
     *  Sets internal NodeFactory pointer of plugins contained in loaded library to NULL, if it's equal to given NodeFactory pointer.
     *  @param nodefactory pointer to NodeFactory
     */
    void removeNodeFactory(const NodeFactory* nodefactory);

    /** Returns 1 if plugin could be requested.
     * @deprecated this method is not used any more 
     * @param plugin name of plugin
     * @return 1 if plugin could be requested, -1 otherwise.
     */
    int getPluginSize(const string& plugin)            const;
  
    /** Sets plugin pointer to NULL.
     * Sets pointer to plugin with given name to NULL.
     * @param plugin name of plugin
     */
    virtual void unregisterPlugin(const std::string& plugin);

  static const char* static_library_name;

/** This method checks if a plugin is excluded
  * \param plugin_name The original plugin name
  * \return Flag which indicates that the plugin is excluded
  */
  virtual bool isPluginExcluded(const string& plugin_name);

//@}

 protected:
    /** @name Get and set parameters.
     * This methods can be accessed through an interface.
     */
    //@{
    /** Returns list of ObjectID's.
     * This method returns the list od ObjectID's that are set in the i'th plugin in a library.
     * The list is only valid if the plugin was registered to a Registry
     * @param library the name of the library
     * @param index the i'th plugin
     * @return list od ObjectID's that are set in the i'th plugin in a library
     */
    virtual list<ObjectID> getPluginID(const string& library, const nmm_uint32& index );

    /** This method returns a list of all liraries that can be loaded.
     *  @return string list of all liraries that can be loaded
     */
    virtual list<string> getPlugins()          const;

    /** This method returns a list of libraries that are currently in use.
     * @return string list of libraries that are currently in use
     */
    virtual list<string> getUsedPlugins()      const;
  
    /** This method returns a list of paths that are searched for libraries.
     * @return string list of paths that are searched for libraries
     */
    virtual list<string> getPluginPath()       const;

    /** This method adds library path which is searched if a library is load. 
     *  The path must be a valid Unix path on the current system.
     *  Path is only added if it is not set already.
     *  @param path path to be add
     */
    virtual void addPluginPath(const string& path);
    //@}


    /** @name Load/remove libraries
     * This methods can be accessed through an interface.
     */
    //@{
    /** This method loads a Plug-In library, register it with a Registry by calling activateRegisryPart()
     * and with a NodeFactory by calling activateFactoryPart().
     * 
     * @param library The name of the library that should be load
     * @sa loadPlugin()
     * @sa activateRegistryPart()
     * @sa activateFactoryPart()
     */
    virtual void loadLibrary(const string& library)
      throw (const SerializedException& );

    /** Loads plug-in library and register the contained plug-in. 
     * This method gets called by method loadLibrary loads a Plug-In library and registers the Plug-In.
     * @sa loadLibrary()  
     * @param library The name of the library of the Plug-In that should be load
     */
    virtual void loadPlugin(const string& library)
      throw (const SerializedException& );
  
    /** This method loads all libraries in a given directory. 
     *  @param path directory which libraries should be loaded
     */        
    virtual void loadExternalPlugins(const string& path)
      throw (const SerializedException& );
  
    /** This method removes all libraries in a given directory. 
     * @param path directory which libraries should be removed
     */        
    virtual void removeExternalPlugins(const string& path)
      throw (const SerializedException& );
    
    /** Removes library with given name.
     * This method removes a library with given name. This method calls the method
     * deactivateRegistryPart() and deactivateFactoryPart().
     * @param library The name of the library that should be removed
     * @sa deactivateRegistryPart()
     * @sa deactivateFactoryPart()
     */
    virtual void removeLibrary(const string& library)
      throw (const SerializedException& );

    /** This method removes the plugins of a library from the registry.
     * @param library The name of the library that should be removed from a registry
     */
    virtual void deactivateRegistryPart(const string& library)
      throw (const SerializedException&);

    /** This method removes the plugins of a library from the NodeFactory.
     * @param library The name of the library that should be removed from a NodeFactory
     */
    virtual void deactivateFactoryPart(const string& library)
      throw (const SerializedException&);

    /** This method registers the plugins of library with a Registry.
     * @param library The name of the library that should be registered with a Registry
     */
    virtual void activateRegistryPart(const string& library)
      throw (const SerializedException&);

    /** This method registers the plugins of library with a NodeFactory.
     * @param library The name of the library that should be registered with a NodeFactory
     */
    virtual void activateFactoryPart(const string& library)
      throw (const SerializedException&);
    //@}

  private: //methods
    /** Initialize the Registry part. 
     *  The plugin must still be loaded.
     *  @param plugin Plugin to be registered at registry
     *  @param registry registry at which plug-in should be registerd
     */
    void activateRegistryPart(const string& plugin, Registry& registry)
      throw (const SerializedException& );

    /** Initialize the NodeFactory part.
     * @param plugin Plugin to be registered at node_factory
     * @param node_factory NodeFactory at which plug-in should be registered
     */
    void activateFactoryPart(const string& plugin, NodeFactory& node_factory)
      throw (const SerializedException& );
  
    /** Reads the content of a directory.
     *  Read content of given folder and return as string list.
     *  @param folder path to folder
     *  @return list of folder content
     */
    list<string> readFolder(const string&  folder)  const
      throw (const SerializedException& );

  private:
    /** @name Constructor & Destructor
     */
    //@{
    /** Constructor
     */
    PluginManager();

    /** Destructor
     */
    virtual ~PluginManager();

    /** Disabled Copy-Constuctor to avoid the instantiation of a second PluginManager
     */
    PluginManager(const PluginManager& );

    /** Adds known ObjectIDs to plugin.
     * @param plugin Plugin which ObjectIDs should be added.
     */
    void applyDefinedObjectIDs(const string& plugin);

#ifdef ENABLE_STATIC_PLUGINS 
    /** Loads static plug-in.
     * @param plugin name of plug-in to be loaded
     */
    void loadStaticPlugin(const string& plugin)
      throw (const SerializedException&);
  
    /** Returns list of names of static plug-ins.
     * @return list of names of static plug-ins
     */
    list<string> getStaticPlugins() const;
#else

    /** Loads dynamic loadable plug-in.
     * @param plugin name of plug-in to be loaded
     */
    void loadDllPlugin(const string& plugin)
      throw (const SerializedException&);

    /** Returns list of names of dynamic loaded plug-ins.
     * @return list of names of dynamic loaded plug-ins
     */
    list<string> getDllPlugins() const;
#endif
    //@}

private:
/** private struct which stores the Dll object, the corresponding Registry and
  * NodeFactory
  */
  struct PluginStruct {
    PluginStruct() : dll_object(0), reg_active(0), fac_active(0), plugin(0) {}
    DynamicLibrary*       dll_object;           ///< The DynamicLibrary object that loads the library
    Registry*    reg_active;                    ///< Stores the Registry which is used to register the plugins
    NodeFactory* fac_active;                    ///< Stores the NodeFactory which is used to create the NodeFactory
    Plugin*      plugin;                        ///< The plugins insde a library, the last element is NULL
    list<string> alias;                         ///< stores alias names of the plugin
  };

  typedef map<string, PluginStruct>::iterator InternalIt;
  typedef map<string, PluginStruct>::const_iterator CInternalIt;

  list<string>              __plugin_path;      ///< Stores a list of pathes wich are searched for libraries
  map<string, PluginStruct> __plugin_map;       ///< Stores the loaded libraries
  map<string, vector<list<ObjectID> > > __defined_id_map;
  
  static const string       __plugin_symbol;    ///< Stores the symbol "nmm_plugins", wich is used to search for plugins in a library
};

  /* Typedef to access the PluginManager with the method call SPluginManager::getInstance() */
  typedef PluginManager::Singleton SPluginManager;

}//end namespace
#include "nmm/nmm_suffix.hpp"

#endif
