/* 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_LOCALREGISTRY_HPP
#define _NMM_LOCALREGISTRY_HPP

#include "nmm/nmm_prefix.hpp"

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

#include "Registry.hpp"
#include "nmm/multimedia/NodeDescription.hpp"
#include "IPluginManager.hpp"

#include "nmm/utils/thread/Thread.hpp"
#include "nmm/utils/Singleton.hpp"

namespace NMM {

class RegistryId;
class Node;
class IRegistry;
class NodeDescription;
                                        
/** LocalRegistry implements a registry that can be used for 'common' nodes.
  * The LocalRegistry implements a registry that can be used for 'common' nodes. A 'common' node
  * is a Node that need no special management. If the Node accesses devices on dynamic bus-system
  * like e.g. FireWire or USB, a specialised Registry should be used.
  *
  * This class is designed as Singleton and can be accessed with the method-call:
  * SLocalRegistry::getInstance();
  */
class LocalRegistry : public Registry
{
DECLARE_SINGLETON(LocalRegistry, CreateUsingNew, DefaultLifetime, MultiThreaded);
/* friend declaration for Singleton class*/
friend class CreateUsingNew<LocalRegistry>;
public:

/** @name registration methods
  * These methods can be used to register/unregister nodes
  */
//@{
/** This method registers a node at LocalRegistry. 
 * A corresponding FactroyObject must be also
 * registered to create an instance of the specific node. The Registry creates a ObjectID
 * which is unique inside this registry and can be used to identify the node.
 * @param np Reference to the NodeDescription
 * @param max_instance maximum number of instances that can be requested
 * @param index index of node plug-in 
 * @return ObjectID of registered node plug-in
 */
  virtual ObjectID registerNode     (const NodeDescription& np, int max_instance, unsigned int index);


/** This method removes a registered node with given ObjectID.
  * @param node_id ObjectID of registered node
  * @throw RegistryException An exception is thrown, if an unexpeceted error occurs.
  */
  virtual void unregisterNode     (const ObjectID& node_id)
          throw (const RegistryException& );
//@}


/** @name Plugin management
  */
//@{
/** This method registers a Plugin.
 * This method registers a Plugin with all contained node description.
 * The generated ObjectID is saved in Plugin.
 * @param plugin Plugin to register
 */
  virtual void registerPlugin(Plugin* plugin);

/** This method registers a Plugin.
 * Unregister all node descriptions contained in given Plugin.
 * @param plugin Plugin to unregister
 */
  virtual void unregisterPlugin(const Plugin* plugin);
  //@}


protected:
/** @name Node Reservation
  * These methods can be used to search and reserve Nodes.
  */
//@{
/** This method searches for a specific node that matches the given NodeDescription. 
 * The NodeDescription is created by an application and stores the information, 
 * like formats and interfaces, that must be supported by the node.
 * @param registry_id The RegistryID of the Registry that should be searched.
 * @param node_desc The NodeDescription that must be supported by the nodes
 * @return A list of ObjectID's from nodes that support the requirements in the NodeDescription
 */
  virtual list<ObjectID >  searchNode(const RegistryID& registry_id, const NodeDescription& node_desc) const;

/** This method reserves a node, which is managed by a Registry. 
 * If the reservation is succesfull, the Registry returns a FactoryTickt 
 * that allows the caller to instantiate a Node by a NodeFactory.
 * @param reg_id The RegistryID of the Registry that should be searched.
 * @param node_id The ObjectID of the Node which is used to identify the Node inside the Registry
 * @param user_info The UserInfo identifies the user that requests the Node. If the node is released, this parameter must be specified again, whereby no other user can release the reserved node.
 * @return FactoryTicket which allows the caller to instantiate a Node by a NodeFactory
 */
  virtual FactoryTicket reserveNode(const RegistryID& reg_id, const ObjectID& node_id, const UserInfo& user_info)
    throw(const RegistryException&);

/** This method creates an instance of a reserved node. 
 * If the node can be created its INode interface is returned. 
 * Otherwise an exception is thrown.
 * @param reg_id The RegistryID of the Registry that should be searched.
 * @param ticket The FactoryTicket must be send to allow the Registry to instantiate the node
 * @param user_info The UserInfo identifies the user that requests the Node. If the node is released this parameter must be specified again, whereby no other user can release the reserved node.
 * @sa destroyNode
 * @return INode interface of reserved node
 */
  virtual INode* instantiateNode(const RegistryID& reg_id, const FactoryTicket& ticket, const UserInfo& user_info)
    throw (const RegistryException&);

/** This method releases a reserved node. 
 * The node is not destroyed, only th reservation is released.
 * @param reg_id The RegistryID of the Registry that should be searched.
 * @param node_id The ObjectID of the reserved node
 * @param user_info The UserInfo identifies the user that requests the Node. If the node is released this parameter must be specified again, whereby no other user can release the reserved node.
 * @sa reserveNode
 */
  virtual void releaseReservation(const RegistryID& reg_id, const ObjectID& node_id, const UserInfo& user_info);

/** This method destroys a created node
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param reference The NMMReference of the instantiated node.
  * @param user_info The UserInfo identifies the user that requests the Node. If the node is released this parameter must be specified again, whereby no other user can release the reserved node.
  * @sa instantiateNode
  */
  virtual void destroyNode(const RegistryID& reg_id, const NMMReference& reference, const UserInfo& user_info);
//@}

  
/** @name Node information
  * These methods can be used to access additional node information 
  */
//@{
  
/** Returns RegistryMemento.
 */
  virtual RegistryMemento getMemento();
  
/** Registers plug-iin with contained NodeDescriptions and set RegistryID like given threw argument.
 * @param rm RegistryMemento to set
 */
  virtual void setMemento(const RegistryMemento& rm);

/** This method returns the maximum instance count for a specific node.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param node_id The ObjectID of the node.
  * @return maximum instance count for given node
  */
  virtual nmm_int32 getMaxInstanceCount(const RegistryID& reg_id, const ObjectID& node_id) const;

/** This method returns the current instance count for a specific node.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param node_id The ObjectID of the node.
  * @return instance count for given node
  */
  virtual nmm_int32 getInstanceCount(const RegistryID& reg_id, const ObjectID& node_id) const;

/** This method returns the library that must be load for a specific node.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param node_id The ObjectID of the node.
  * @return library represented by string that must be load for a specific node
  */
  virtual string getPluginInfo(const RegistryID& reg_id, const ObjectID& node_id) const;

/** This method returns all ObjectIDs of nodes that are available.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @return list of ObjectIDs of nodes that are available
  */
  virtual list<ObjectID >  getNodeList(const RegistryID& reg_id) const;

/** This method returns all ObjectIDs of nodes that are currently reserved and not available.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @return list of all ObjectIDs of nodes that are currently reserved and not available
  */
  virtual list<ObjectID >  getReservedNodeList(const RegistryID& reg_id) const;

/** This method returns a short NodeInfo for all nodes that are managed in a specific registry.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @return short NodeInfo for all nodes that are managed in the specified registry
  */
  virtual list<NodeInfo> getNodeInfoList(const RegistryID& reg_id) const;

/** This method returns a short NodeInfo for all nodes that are managed in a specific registry.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @return short NodeInfo for all nodes that are managed in the specified registry
  */
  virtual list<NodeDescription> getNodeDescriptionList(const RegistryID& reg_id);
 
  
/** This method returns the complete NodeDescription of a specifc node.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param node_id The ObjectID of the node.
  * @return complete NodeDescription of the specified node
  */
  virtual NodeDescription getNodeDescription(const RegistryID& reg_id, const ObjectID& node_id) const;

/** This method returns the NodeFactoryID of the NodeFactory that can instantiate a specific node.
  * @param reg_id The RegistryID of the Registry that should be searched.
  * @param node_id The ObjectID of the node.
  * @return NodeFactoryID of the NodeFactory that can instantiate the specified node
  */
  virtual NodeFactoryID getNodeFactoryID(const RegistryID& reg_id, const ObjectID& node_id) const;
//@}

/** @name Reset Registry
  * These methods can be used to reset a registry
  */
//@{
/** This method releases ALL nodes.
 */
  virtual void resetInternalReservation();

/** This method releases ALL nodes reserved by specifc user.
 * @param user_info reference to UserInfo that describes user which nodes should be released.
 */
  virtual void resetSpecificInternalReservation(const UserInfo& user_info);
//@}

private:
/** @name Constructor/Detructor
  */
//@{
/** Constructor
  */
  LocalRegistry();

/** Disabled Copy-Constructor to avoid creation of another instance
  */
  LocalRegistry(const LocalRegistry& );

/** Destructor
  */
  virtual ~LocalRegistry();
//@}

/** Get ObjectID's of all nodes with given plug-in name.
 * @param plugin name of plug-in
 * @return vector of lists of ObjectID's belonging to given plug-in
 */
  vector<list<ObjectID> > getObjectIDList(const string& plugin);

  /** The InternalStruct stores all information needed to manage a Node
    */
  struct InternalStruct {
    InternalStruct() : instance_count(0), max_instance_count(0) {}
    NodeDescription property;                       ///< stores the NodeDescription
    unsigned int instance_count;                    ///< stores the current instance count
    int max_instance_count;                         ///< stores the maximum instance count
    unsigned int plugin_index;                      ///< stores the position in the plugin
  };
private: //attributes
  typedef map<ObjectID, InternalStruct*>::iterator InternalIt;
  typedef map<ObjectID, InternalStruct*>::const_iterator CInternalIt;

  map<ObjectID, InternalStruct*> __registered_nodes;   ///< Stores the Nodes managed by this Registry
  multimap<ObjectID, UserInfo>   __reserved_nodes;    ///< Associate the ObjectID with an UserInfo
  map<NMMReference, UserInfo>    __allocated_nodes;   ///< Stores the NMMReference of nodes allocated by this registry
  ObjectID                       __node_id_counter;   ///< Counter used to create ObjectID's
  bool                           __use_memento;       ///< true if setMemento has been called, false otherwise
  IPluginManager_var             __internal_plugin_manager; ///< internal PluginManager
};

/* Typedef to access the LocalRegistry with the method call SLocalRegistry::getInstance() */
typedef LocalRegistry::Singleton SLocalRegistry; 
}//end namespace
#include "nmm/nmm_suffix.hpp"

#endif

