/* 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_PLUGIN_HPP
#define NMM_PLUGIN_HPP NMM_PLUGIN_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/comm/ObjectID.hpp"

#include "nmm/comm/messaging/IEventReceiver.hpp"
#include "nmm/Exception.hpp"
#include "nmm/comm/serialize/SerializedException.hpp"

#include <list>
#include <string>
#include <vector>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "visibility.hpp"

#ifdef ENABLE_STATIC_PLUGINS

#define NMM_REGISTER_PLUGIN_IMPL(node, ns, name) \
using namespace ns; \
static NMM::TPlugin<node> plugin(name); \
NMM::Plugin* node##_plugin = &plugin;

#define NMM_REGISTER_SPECIAL_PLUGIN_IMPL(node, ns, plugin) \
using namespace ns; \
NMM::Plugin* node##_plugin = &plugin;

#else

#ifdef WIN32
#define NMM_REGISTER_PLUGIN_IMPL(node, ns, name) \
using namespace ns; \
static NMM::TPlugin<node> plugin(name); \
extern "C" { __declspec(dllexport) NMM::Plugin* nmm_plugins = &plugin; }
#else
#define NMM_REGISTER_PLUGIN_IMPL(node, ns, name) \
using namespace ns; \
static NMM::TPlugin<node> plugin(name); \
NMM::Plugin* nmm_plugins = &plugin; 
#endif

#ifdef WIN32
#define NMM_REGISTER_SPECIAL_PLUGIN_IMPL(node, ns, plugin) \
using namespace ns; \
extern "C" { __declspec(dllexport) NMM::Plugin* nmm_plugins = &plugin;}
#else
#define NMM_REGISTER_SPECIAL_PLUGIN_IMPL(node, ns, plugin) \
using namespace ns; \
NMM::Plugin* nmm_plugins = &plugin;
#endif

#endif

/** Register a plug-in.
 * This macro must be inserted in a plug-in library to register a plug-in defined within that library.
 * It can only be used for standard plug-ins which do not require a special Plugin class.
 *
 * Example:
 * @code
 * NMM_REGISTER_PLUGIN(DevNullNode, NMM, "DevNullNode")
 * @endcode
 *
 * @param node The name of the node class
 * @param ns The namespace of the node class
 * @param name The name of the plug-in
 *
 * @see Plugin
 */
#define NMM_REGISTER_PLUGIN(node, ns, name) NMM_REGISTER_PLUGIN_IMPL(node, ns, name)

/** Register a special plug-in.
 * This macro must be inserted in a plug-in library to register a plug-in defined within that library.
 * It is used for plug-ins which require a special Plugin class, such as plug-ins which probe for devices
 * upon registration.
 *
 * Example:
 * @code
 * NMM_REGISTER_PLUGIN(ALSAPlaybackNode, NMM, ALSAPlaybackNodePlugin)
 * @endcode
 *
 * @param node The name of the node class
 * @param ns The namespace of the node class
 * @param plugin The plug-in class which registers the plug-in
 *
 * @see Plugin
 */
#define NMM_REGISTER_SPECIAL_PLUGIN(node, ns, plugin) NMM_REGISTER_SPECIAL_PLUGIN_IMPL(node, ns, plugin)

namespace NMM {

  class Exception;
  class INode;
  class NodeDescription;
  class Node;
  class IPluginManagerHandle;
  class FactoryObject;

  /** Representation of a plugin.
   * For each registered plug-in class there exists at least on Plugin object.
   * Plugin objects are created when the library containing the corresponding plug-in class is loaded
   * and are managed by the PluginManager. Upon registration, the Plugin object creates one or more
   * instances of the plug-in class for one or more different configurations. Multiple configurations
   * of a plug-in are used for device management.
   *
   * This is an abstract class. For standard plug-ins, the TPlugin template class is used.
   * Plug-ins which represent devices are managed by a subclass of the TDevicePlugin class.
   *
   * Applications and plug-in libraries do not create Plugin objects manually.
   * Instead, the macros NMM_REGISTER_PLUGIN and NMM_REGISTER_SPECIAL_PLUGIN should be used.
   * Special plug-in classes should be derived from TPlugin or TDevicePlugin.
   *
   * @see PluginManager
   */
  class DLLEXPORT Plugin : private IEventReceiver {
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Create a plug-in.
     *
     * @param node_name The name of the node
     * @param max_instances Maximum number of instances. A value of -1 means no limitation.
     */
    Plugin(const std::string& node_name, int max_instances);

    /** Virtual destructor.
     */
    virtual ~Plugin();
    //@}

    /** @name Plug-in Registration
     */
    //@{
    /** Plug-in activation.
     * This method is called by the PluginManager when the plug-in is activated.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     *
     * @exception SerializedException if an error occurred
     * @exception Exception if an error occurred
     */
    virtual void nmmInit()
      throw(const Exception&, const SerializedException&) = 0;

    /** Plug-in deactivation.
     * This method is called by the PluginManager when the plug-in is deactivated.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     * This method may not throw an exception.
     */
    virtual void nmmFini() = 0;

    /** Plug-in registration.
     * This method is called by the PluginManager when the plug-in is registered.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     *
     * @exception SerializedException if an error occurred
     * @exception Exception if an error occurred
     */
    virtual void nmmRegistryInit()
      throw(const Exception&, const SerializedException&) = 0;

    /** Plug-in deregistration.
     * This method is called by the PluginManager when the plug-in is deregistered.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     * This method may not throw an exception.
     */
    virtual void nmmRegistryFini() = 0;

    /** Plug-in factory registration.
     * This method is called by the PluginManager when the node is registered at the NodeFactory.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     *
     * @exception SerializedException if an error occurred
     * @exception Exception if an error occurred
     */
    virtual void nmmFactoryInit()
      throw(const Exception&, const SerializedException&) = 0;

    /** Plug-in factory deregistration.
     * This method is called by the PluginManager when the node is deregistered at the NodeFactory.
     * It can be implemented by subclasses to perform specific actions required for a particular plug-in.
     * This method may not throw an exception.
     */
    virtual void nmmFactoryFini() = 0;

    /** Get factory object.
     * This method returns a factory object for this plug-in.
     * The caller is responsible for deleting the returned FactoryObject.
     *
     * @return A new factory object for this plug-in
     */
    virtual FactoryObject* getFactoryObject() = 0;
    //@}


    /** @name Plug-in information
     */
    //@{
    /** Get the library name of this plug-in.
     *
     * @return The library name
     */
    inline const std::string& getLibraryName()                const { return __library_name;}

    /** Get the node name of this plug-in.
     *
     * @return The node name
     */
    inline const std::string& getNodeName()                   const { return __node_name; }

    /** Get the NMM version of this plug-in.
     * This method returns the NMM version which was used to compile the plug-in.
     *
     * @return The NMM version
     */
    inline const std::string& getNMMVersion()                 const { return __nmm_version; }

    /** Get the minor NMM version.
     * This method returns the minor number of the NMM version which was used to compile the plug-in.
     *
     * @return The minor number of the NMM version
     */
    inline int           getMinorNumber()                     const { return __nmm_minor_nbr;}

    /** Get the major NMM version.
     * This method returns the major number of the NMM version which was used to compile the plug-in.
     *
     * @return The major number of the NMM version
     */
    inline int           getMajorNumber()                     const { return __nmm_major_nbr;}

    /** Get the micro NMM version.
     * This method returns the micro number of the NMM version which was used to compile the plug-in.
     *
     * @return The micro number of the NMM version
     */
    inline int           getMicroNumber()                     const { return __nmm_micro_nbr;}

    /** Get the object ID of a specific object.
     * This method returns the object ID of an object which can be created by this plug-in.
     * The object is specified by its index, which must be between 0 and this->getObjectID()-1 inclusively.
     *
     * @param index An object index
     * @return The object ID of the object with the given index
     */
    inline const ObjectID& getObjectID(unsigned int index)    const { return __id_vector[index]; }

    /** Get the number of objects.
     * This method returns the number of objects which can be created by this plug-in.
     *
     * @return The number of objects
     */
    inline size_t getSize()                             const { return __id_vector.size(); }

    /** Get an interface to the plug-in manager.
     * This method returns an interface to the PluginManager which can be used to unregister the plug-in
     * or identify the PluginManager which owns the plug-in.
     * The returned object must not be deleted by the caller.
     *
     * @return An interface to the plug-in manager
     */
    inline IPluginManagerHandle* getPluginManager()           const { return __handle; }

    /** Get the list of node descriptions.
     * This method returns a list of the node descriptions of all nodes that can be created by this plug-in
     * and the maximum number of instances of each node.
     * Each entry of the list represents a single node and is a pair of the node description and the maximum
     * number of instances.
     *
     * @return The list of node descriptions
     */
    inline const std::list<std::pair<NodeDescription, int> >& getNodeDescriptionList()  const { return __nodedescription_list; }
    //@}

    /** @name Internal Methods
     */
    //@{
    /** Add an object.
     * This method is used internally by the PluginManager and should not be called by applications or plug-ins.
     *
     * @param object_id The object ID of the new object
     */
    inline void addObjectID(const ObjectID& object_id)    { __id_vector.push_back(object_id); }

    /** Set the library name of this plug-in.
     * This method is used internally by the PluginManager and should not be called by applications or plug-ins.
     *
     * @param library_name The library name
     */
    inline void setLibraryName(const std::string& library_name)         { __library_name = library_name;}

    /** Set the index of this plug-in.
     * This method is used internally by the PluginManager and should not be called by applications or plug-ins.
     *
     * @param index The plug-in index
     */
    inline void setPluginIndex(unsigned int index)                      { __index = index; }

    /** Set the interface to the plug-in manager.
     * This method is used internally by the PluginManager and should not be called by applications or plug-ins.
     *
     * @param handle The interface to the plug-in manager
     */
    inline void setPluginManager(IPluginManagerHandle* handle)          { __handle = handle; }

    /** Get the index of this plug-in.
     * This method is used internally by the PluginManager and should not be called by applications or plug-ins.
     *
     * @return The plug-in index
     */
    inline unsigned int getPluginIndex()                       const    { return __index;  }  
    //@}

  protected:
    /** @name Developer Methods
     * These methods are for the plugin developer and offer extendend functionality to simplify
     * the development of a node-specific plugin.
     */
    //@{
    /** Prepare a node interface for registration.
     * This method prepares a node interface for the registration. It enables event logging to
     * receive configuration events for a node. These events are stored with the node description to
     * restore the node with the registered configuration.
     *
     * @param node The node to be registered
     * @return An interface to the node which is used for event logging
     */
    INode* prepareRegistration(Node& node);

    /** Create a node description.
     * This method creates a NodeDescription for a specific node.
     *
     * @param node A node interface for the node
     * @param description Returns the node description
     */
    void createNodeDescription(INode& node, NodeDescription& description);

    /** Register a node.
     * This method adds the given node description to the list of node descriptions of this plug-in.
     * It also stores the maximum number of instances along with the node description.
     *
     * @param description A node description of the node
     * @param max_instances Maximum number of instances of the node
     */
    void registerNode(const NodeDescription& description, int max_instances);

    /** Get the maximum number of instances of this plug-in.
     * @return The maximum number of instances of this plug-in
     */
    inline int getMaxInstanceCount()                       const    { return __max_instance;}
    //@}

  private:
    /** @name Internal Event Logging Methods
     */
    //@{
    /** Receive an event.
     * This implementation is used for logging events from a plug-in to store within the node description.
     *
     * @param event The event which was sent to the node
     * @param sender The sender of the event
     * @return SUCCESS
     */
    virtual Result receiveEvent(Event& event, const IEventSender* sender);

    /** Disable event logging.
     *
     * @param node The node for which event logging should be disabled
     */
    void disableEventLogging(INode& node);

    /** Get the temporary list of configuration events.
     *
     * @return The temporary list of configuration events
     */
    inline        std::list<Event>& getConfigEvents()              { return __config_events; }

    /** Get the temporary list of configuration events.
     *
     * @return The temporary list of configuration events
     */
    inline  const std::list<Event>& getConfigEvents()      const   { return __config_events; }
    //@}

  private:
    std::string             __library_name;     ///< Library name
    std::string             __nmm_version;      ///< NMM version used to compile this plug-in
    int                     __nmm_major_nbr;    ///< NMM major number version used to compile this plug-in
    int                     __nmm_minor_nbr;    ///< NMM minor number version used to compile this plug-in
    int                     __nmm_micro_nbr;    ///< NMM micro number version used to compile this plug-in
    std::vector<ObjectID>   __id_vector;        ///< List of all objects
    mutable std::list<Event> __config_events;   ///< Temporary list of logged node configuration events
    std::string             __node_name;        ///< Node name
    int                     __max_instance;     ///< Maximum instance count
    unsigned int            __index;            ///< Plug-in index
    IPluginManagerHandle*   __handle;           ///< Interface to the PluginManager
    std::list< std::pair<NodeDescription, int> > __nodedescription_list;  ///< List of all node descriptions
  };


}

#include "nmm/nmm_suffix.hpp"

#endif
