/* 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_REGISTRY_HPP
#define NMM_REGISTRY_HPP

#include "nmm/nmm_prefix.hpp"

#include "IRegistryImpl.hpp"
#include "nmm/utils/NamedObject.hpp"
#include "nmm/utils/thread/ThreadCondition.hpp"

#include <list>
#include <string>

namespace NMM {

  class Node;
  class RegistryException;
  class RegistryID;
  class NodeDescription;
  class Plugin;


  /** Base class for all Registries. 
   * Offers Registry management for child registires only
   */
  class Registry : virtual public IRegistryImpl, public NamedObject {

  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Constructor.
     *
     * @param name name of Registry
     * @param parent parent registry
     */
    Registry(const char* name, Registry* parent = 0);

    /** Destructor.
     */
    virtual ~Registry();
    //@}


    /** @name Add/remove Registries
     */
    //@{
    /** This method adds an registry as child of this registry to this registry.
     *
     * @param reg child registry to add
     */
    void addRegistry(Registry& reg);

    /** This method removes a child registry from this registry.
     *
     * @param reg The registry that should be removed
     */
    void removeRegistry(Registry& reg);

    /** This method sets the Parent Registry of this object. 
     *
     * This method is for internal use!
     *
     * @param reg Pointer to the parent-registry.
     */
    void setParent(Registry* reg);

     /** This method returns the RegistryID which is unique in the network.
     * 
     * @return RegistryID of this Registry
     */
    virtual const RegistryID& getOwnRegistryID()      const {return __registry_id; }
    //@}

    /** @name Event processing
     */
    //@{
    /** This method processes an event.
     *
     * @param event Event to receive
     * @param sender sender of event
     * @return event result
     */
    virtual Result receiveEvent(Event& event, const IEventSender* sender = 0) ;
    //@}

    /** @name Plugin management
     */
    //@{
    /** This method registers a Plugin.
     *
     * @param plugin Plugin to register
     */
    virtual void registerPlugin(Plugin* plugin)  = 0;

    /** This method registers a Plugin.
     *
     * @param plugin Plugin to register
     */
    virtual void unregisterPlugin(const Plugin* plugin) = 0;
    //@}

    /* Udpates the NodeDescription from the application.
     *
     * This method udpates the NodeDescription from the application (nd_app) with the
     * additional information from the NodeDescription (nd_reg).
     *
     * @param nd_app NodeDescription to be updated
     * @param nd_reg NodeDescription to take information for update from
     */
  static void updateNodeDescription(NodeDescription& nd_app, const NodeDescription& nd_reg);

protected: //methods

    /** @name Search and request nodes
     * These methods can be accessed through the IRegistry-interface
     */
    //@{
    /** This method requests a Node and returns its INode interface. 
     *
     * If the Node can not be requested an exception is thrown. 
     * This method calls the method reserveNode() to reserve the Node
     * and instantiateNode() to create it.
     *
     * @param reg The RegistryID of the Registry which manages the node.
     * @param node_id The ObjectID of the Node that is requested.
     * @param info The UserInfo of the application that requests the node.
     * @param nd NodeDescription of requested node
     * @return INode interface for requested Node
     * @sa reserveNode()
     * @sa instantiateNode()
     */
  virtual INode* requestNode(const RegistryID& reg, const ObjectID& node_id, const UserInfo& info, NodeDescription& nd)
    throw(const RegistryException&);

    /** This method requests a Object and returns its interface. 
     * 
     * If the objecte can not be requested
     * an exception is thrown. 
     *
     * @param reg The RegistryID of the Registry which manages the node.
     * @param node_id The ObjectID of the object that is requested.
     * @param info The UserInfo of the application that requests the object.
     * @return Interface for requested object
     * @sa reserveNode()
     * @sa instantiateNode()
     */
  virtual Interface* requestObject(const RegistryID& reg, const ObjectID& node_id, const UserInfo& info) 
    throw(const RegistryException&);

    /** This method releases a requested Node. 
     * 
     * This method calls the method releasReservation() to
     * to release the reservation and destroyNode() to delete it.
     *
     * @param reg_id The RegistryID of the Registry which manages the node.
     * @param ref NMMReference to node
     * @param node_id The ObjectID of the Node that is released.
     * @param user_info info The UserInfo of the application that requested the node.
     * @sa releaseReservation()
     * @sa destroyNode()
     */
  virtual void releaseNode(const RegistryID& reg_id, const NMMReference& ref, const ObjectID& node_id, const UserInfo& user_info);

   /** This method releases a requested object. 
     *
     * @param reg_id The RegistryID of the Registry which manages the object.
     * @param ref The NMMReference of the object that is released.
     * @param node_id The ObjectID of the object that is released.
     * @param user_info The UserInfo of the application that requested the object.
     */
  virtual void releaseObject(const RegistryID& reg_id, const NMMReference& ref, const ObjectID& node_id, const UserInfo& user_info);

    /** This method searches for Nodes with certain capabilities.
     *
     * This method searches for Nodes with certain capabilities, which are described in the NodeDescription.
     *
     * @param node_desc The NodeDescription with the requirements of a searched node.
     * @return list of Responses for request
     */
  virtual list<Response >  initRequest(const NodeDescription& node_desc) const;

    /** This method searches for objects with certain capabilities.
     *
     * This method searches for objects with certain capabilities, which are described in the
     * ObjectDescription.
     *
     * @param node_desc The ObjectDescription with the requirements of a searched object.
     * @return list of Responses for request
     */ 
  virtual list<Response >  initObjectRequest(const ObjectDescription& node_desc) const;

//@}

    /** @name Enable/disable access control
     * These methods can be accessed through the IRegistry-interface.
     */
    //@{
    /** This method enables the access control. 
     *
     * If the access control is enabled, no other
     * application can access the Registry.
     *
     * @param info The UserInfo of the application that tries to enable the access control.
     */
  virtual void initTransaction(const UserInfo& info)
     throw(const RegistryException&);

    /** This method disables the access control.
     * 
     * This method disables the access control, so other applications can access the Registry again.
     *
     * @param info The UserInfo of the application that tries to disable the access control.
     */
  virtual void closeTransaction(const UserInfo& info);
//@}


    /** @name Access Child-Registries
     * These methods can be accessed through the IRegistry-interface
     */
    //@{
    /** This method returns the RegistryID of this Registry.
     *
     * @return RegistryID
     */
    virtual RegistryID getRegistryID()      const {return __registry_id; }

    /** This method returns the RegistryID's of all child registries.
     *
     * @param registry_id RegistryID to request list of child registries for
     * @return list of RegistryIDs of all child registries
     */
    virtual list<RegistryID >  getChildList(const RegistryID& registry_id) const;

    /** Tests if Registry is managed gy this Registry or by one of its childs.
     *
     * Tests if the Registry with given RegistryID is managed by this
     * Registry or by one the children of the Registry.
     *
     * @param reg_id The RegistryID of the searched Registry.
     * @return true if Registry is managed gy this Registry or by one of its childs
     */
    virtual bool isSubRegistry(const RegistryID& reg_id) const;
    //@}

  protected:
    /** @name Access Child-Registries
     */
    //@{
    /** This mehtod returns the IRegistry for the given RegistryID.
     *
     * @param reg_id The RegistryID of the searched Registry
     * @return pointer to IRegistry for the given RegistryID
     */
    virtual       IRegistry* findRegistry(const RegistryID& reg_id);

    /** This mehtod returns the IRegistry for the given RegistryID.
     *
     * @param reg_id The RegistryID of the searched Registry
     * @return IRegistry for the given RegistryID
     */
    virtual const IRegistry* findRegistry(const RegistryID& reg_id) const;

    /** This method returns a list of all child registries. 
     *
     * This method is obsolete.
     *
     * @sa getIChildList
     * @return list of all child registries.
     */
    inline  const list<Registry*>& getChildList()           const { return __child_list; }

     /** This method returns a list of all child registries. 
     *
     * This method is obsolete.
     *
     * @sa getIChildList
     * @return list of all child registries.
     */
    inline        list<Registry*>& getChildList()                 { return __child_list; }

    /** This method returns a list with interfaces of all child registries.
     *
     * @return list with interfaces of all child registries
     */
    inline  const list<IRegistry*>& getIChildList()         const { return __i_child_list; }

    /** This method returns a list with interfaces of all child registries.
     *
     * @return list with interfaces of all child registries
     */
    inline        list<IRegistry*>& getIChildList()               { return __i_child_list; }

    /** This method returns a pointer to the parent registry which manages this Registry.
     *
     * @return pointer to the parent registry which manages this Registry
     */
    inline  const Registry* getParent()                     const { return __parent; }

    /** This method returns a pointer to the Parent-Registry which manages this Registry
     *
     * @return pointer to the parent registry which manages this Registry
     */
    inline        Registry* getParent()                           { return __parent; }
    //@}

    /** @name Access Transaction-Sender
     */
    //@{
    /** This method sets the new transaction sender.
     *
     * @param sender The Pointer to the sender which enables the access control.
     */
    inline  void  setTransactionSender(const IEventSender* sender){ __transaction_sender = const_cast<IEventSender*>(sender); }

    /** This method returns the sender which enables the access control. 
     *
     * If the sender is NULL no transaction control is enabled.
     *
     * @return sender which enables the access control
     */
    inline  const IEventSender* getTransactionSender()      const { return __transaction_sender; }

    /** This method returns the sender which enables the access control. 
     *
     * If the sender is NULL no transaction control is enabled.
     *
     * @return sender which enables the access control
     */
    inline        IEventSender* getTransactionSender()            { return __transaction_sender; }
    //@}

    /** @name Internal methods
     */
    //@{
    /** This method must be used to set the is_valid flag in given FactoryTicket to true.
     *
     * This method must be called to create a valid FactoryTicket.
     *
     * @param ft FactoryTicket to set valid
     */
    inline  void  setTicketValid(FactoryTicket& ft)               { ft.setValid(true); }

    /** This method sets RegistryID.
     *
     * @param reg_id RegistryID to set
     */
    inline  void  setRegistryID(const RegistryID& reg_id)         { __registry_id = reg_id; }
    //@}

    /** This template method can be used to get informed whenever a new child registry is added. 
     *
     * @param reg_id RegistryID of new registry
     */
    virtual void announceRegistry( const RegistryID&  reg_id);
  
    /** Announce this registry as new to all parent registries.
     */
    void propagateRegistry(); 

  private:

    /** This mehtod waits until a transaction is closed.
     *
     * If the transaction sender is set and is unequal to given sender we must wait.
     *
     * @param sender sender of transaction to wait for
     */
    void waitForTransaction(const IEventSender* sender) const;

  private:
    list<Registry*>                 __child_list;                   ///< Stores the sub_registries (should be replaced)
    list<IRegistry*>                __i_child_list;                 ///< Stores the interfaces to the sub_registries
    Registry*                       __parent;                       ///< Stores the pointer to the parent
    RegistryID                      __registry_id;                  ///< Stores the RegistryID which is unique in a network

    mutable IEventSender*           __transaction_sender;           ///< The current transaction sender
    UserInfo                        __locked_user;                  ///< The UserInfo of the application that enabels the transaction
    ThreadMutex                     __transaction_mutex;            ///< Mutex used for transaction-condition
    ThreadCondition                 __transaction_condition;        ///< ThreadCondition to wait for locked transaction
  };

  inline void Registry::announceRegistry( const RegistryID&) {}
}//end namespace
#include "nmm/nmm_suffix.hpp"

#endif
