/* 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_SERVERREGISTRY_HPP
#define NMM_SERVERREGISTRY_HPP

#include "nmm/nmm_prefix.hpp"

#include "Registry.hpp"
#include "IServerRegistryImpl.hpp"
#include "Session.hpp"

#include "nmm/utils/thread/ThreadedObject.hpp"

#include <list>

#include "visibility.hpp"

namespace NMM {
  class GraphDescription;
  class Synchronizer;
  class Session;
  class ISession;
  class UDPBroadcaster;
  
  /** \class ServerRegistry
   * An instance of class ServerRegistry manages and provices access to different kind of Registries (e.g. LocalRegistry) that 
   * are called child registries. A child registry manages a certain group of Nodes. In general a single ServerRegistry 
   * should run on a system using the application serverregistry to ensure a correct management of available Nodes. However,
   * an instance of class ServerRegistry can also run within an application which is especially usefull if only a single appliction
   * runs on a (e.g. embedded) system. 
   */
  class DLLEXPORT ServerRegistry : public Registry, public IServerRegistryImpl, protected ThreadedObject {
  public:

    /** Constructor.
     *
     * @param security_checks specifies if security checks should be done
     */
    ServerRegistry(bool security_checks = true);
  
    /** Destructor.
     */
    virtual ~ServerRegistry();
  
    /** Writes RegistryMementos of all children of this Registry to given OValueStream.
     *
     * @param ov OValueStream to write to
     */
    void writeToStream(OValueStream& ov);
  
    /** Reads RegistryMementos from given IValueStream and set them in child registries.
     *
     * @param iv IValueStream to read from
     */
    void readFromStream(IValueStream& iv);

    /** Since a ServerRegistry does not manages Nodes, this is an empty implementation.
     *
     * @param plugin Plugin to register
     */
    virtual void registerPlugin(Plugin* plugin);

    /** Since a ServerRegistry does not manages Nodes, this is an empty implementation.
     *
     * @param plugin Plugin to unregister
     */
    virtual void unregisterPlugin(const Plugin* plugin);
 
    /** Sets UDPBroadcaster and registers this object as event receiver.
     *
     * @param broadcaster UDPBroadcaster used to broadcast ServerRegistry information
     * @param port port on which ServerRegistry listens for incoming connections
     */
    void setUDPBroadcaster(UDPBroadcaster* broadcaster, int port);

  protected: //IRegistry interface methods

    /** @name Node Reservation
     * These methods can be used to search and reserve Nodes and can be accessed with the IRegistry-Interface.
     */
    //@{
    /** Searches for Nodes with capabilities described in given NodeDescription.
     *
     * 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 
     */
    virtual list<Response >  initRequest(const NodeDescription& node_desc) const;

    /** Requests a Node.
     *
     * 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 describing the node
     * @return requested Node
     * @sa reserveNode()
     * @sa instantiateNode()
     */
    virtual INode* requestNode(const RegistryID& reg, const ObjectID& node_id, const UserInfo& info, NodeDescription& nd)
      throw(const RegistryException&);

    /** Releases a requested Node.
     *
     * @param reg_id The RegistryID of the Registry which manages the node.
     * @param ref The NMMReference of the Node that is released.
     * @param node_id The ObjectID of the Node that is released.
     * @param user_info The UserInfo of the application that requested the node.
     */
    virtual void releaseNode(const RegistryID& reg_id, const NMMReference& ref, const ObjectID& node_id, const UserInfo& user_info);

    /** Releases a requested Node.
     *
     * @param inode INode interface for Node to be released
     */
    virtual void releaseSharedNode(const INode& inode);

    /** Returns list of nodes matching given NodeDescription.
     *
     * 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;

    /**  Reserves a node which is managed by a Registry.
     *
     * 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.
     */
    virtual FactoryTicket reserveNode(const RegistryID& reg_id, const ObjectID& node_id, const UserInfo& user_info)
      throw(const RegistryException&);

    /** Creates an instance of a reserved node.
     *
     * 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
     */
    virtual INode* instantiateNode(const RegistryID& reg_id, const FactoryTicket& ticket, const UserInfo& user_info)
      throw (const RegistryException&);

    /** Releases a reserved node.
     *
     * 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 and can be accessed with
     * the IRegistry interface.
     */
    //@{
    /** 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.
     */
  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.
     */
  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 that must be loaded for specified 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 ObjectIDs of all 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 ObjectIDs of all 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 list of NodeInfos of all nodes managed in specified registry
     */
    virtual list<NodeInfo> getNodeInfoList(const RegistryID& reg_id) const;

    /** 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 NodeDescription of specified node
     */
    virtual NodeDescription getNodeDescription(const RegistryID& reg_id, const ObjectID& node_id) const;

    /** This method returns all NodeDescriptions managed by a Registry.
     *
     * @param reg_id The RegistryID of the Registry that should be searched.
     * @return list of all NodeDescriptions stored by specified registry
     */
    virtual list<NodeDescription> getNodeDescriptionList(const RegistryID& reg_id);

    /** This method returns the memento object of a registry.
     *
     * @param reg_id The RegistryID of the Registry that should be searched.
     * @return memento object of specified registry
     */
    virtual RegistryMemento getMemento(const RegistryID& reg_id);

    /** This method returns the memento object of this registry.
     *
     * @return memento object of this registry
     */
    virtual RegistryMemento getMemento();

    /** 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 specified node
     */
    virtual NodeFactoryID getNodeFactoryID(const RegistryID& reg_id, const ObjectID& node_id) const;

    /** Empty implementation.
     *
     * @param reg_memento RegistryMemento to set
     */
    virtual void setMemento(const RegistryMemento& reg_memento);

    /** Tests if Registry is managed by this Registry or on of its children.
     *
     * This method returns true, 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 manager by this Registry or one of its children, false otherwise
     */
  virtual nmm_bool isSubRegistry(const RegistryID& reg_id) const;
//@}

    /** @name Enable/disable access control
     * These methods can be accessed through the IRegistry-interface and can be accessed with
     * 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, 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 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 specific user.
     *
     * @param user_info UserInfo specyfying user which Nodes should be released
     */
    virtual void resetSpecificInternalReservation(const UserInfo& user_info);
    //@}
  protected: //IServerRegistry interface

    /** @name Access leasing parameter
     * These methods can be used to access the leasing parameter and can be accessed with the
     * IServerRegistry-Interface.
     */
    //@{

/** This method returns the current system time  */
    virtual Time getSystemTime();

    /** This method returns the timeout for access control.
     *
     * @return timeout for access control
     */
  virtual  nmm_uint32 getTransactionTimeout();

    /** This method sets the timout for access control.
     *
     * @param time_out The new timeout for the access control
     */
  virtual void setTransactionTimeout(const  nmm_uint32& time_out);

    /** This method returns the leasing timeout for nodes.
     *
     * @return leasing timeout for nodes
     */
  virtual  nmm_uint32 getNodeLeasingTime();

    /** This method sets the leasing time_out for nodes.
     *
     * @param time_out The new timeout for nodes.
     */
  virtual void setNodeLeasingTime(const nmm_uint32& time_out);

    /** This method refreshes the reservation of a leased nodes.
     *
     * @param reg_id RegistryID of registry node is reserved at
     * @param node_id ObjectID of node
     * @param user_info UserInfo of node
     * @return new reservation time of node
     */
  virtual  nmm_uint32 refreshNodeLeasing(const RegistryID& reg_id, const ObjectID& node_id, const UserInfo& user_info);

    /** This method refreshes the enabled access control.
     * 
     * @param user_info UserInfo specifying user which locked registry
     * @return new transaction time if successful, 0 otherwise 
     */
    virtual unsigned int refreshTransaction(const UserInfo& user_info);
    //@}

    /** Requests NMMObject specified by given ObjectDescription.
     *
     * Currently can only return MultiAudioVideoSynchronizer.
     *
     * @param od ObjectDescription specifying NMMObject to request
     */
    virtual Interface* requestNMMObject(const ObjectDescription& od)
      throw(const Exception&);

    /** Release NMMObject specified by given Interface.
     *
     * @param i_face Interface specifying NMMObject 
     */
    virtual void releaseNMMObject(const Interface& i_face)
      throw(const Exception&);

    /* Begin > Session sharing interface */
    /** Initializes new session with synchronizer.
     *
     * @param gd GraphDescription of graph to create session for
     * @param u_info UserInfo describing user initializing session
     * @param given_session Interface to master session or NULL, if this is the master session
     * @return ISession interface to created session
     */
    ISession* initSession(GraphDescription& gd, const UserInfo& u_info, const ISession* given_session);

    /** Releases given session.
     *
     * @param session session to release
     * @param user_info UserInfo which tries to release session
     */
    void releaseSession(const ISession& session, const UserInfo& user_info);

  void searchSharedGraphs(GraphDescription& query_graph, GraphDescription&);

    /** Requests nodes contained in given GraphDescription.
     *
     * @param gd GraphDescription with Nodes to request
     * @param user_info UserInfo specifying user requesting graph
     */
    void requestGraph(GraphDescription& gd, const UserInfo& user_info);

    /** Sets sharing type on nodes and return session for shared graph.
     *
     * @param gd GraphDescription for shared graph
     * @param user_info UserInfo specifying user requesting graph
     */
    ISession* requestSharedGraph(GraphDescription& gd, const UserInfo& user_info)throw (const NMM::RegistryException&);

    /** Empty Implementation.
     *
     * @param gd graph to be release
     * @param user_info user releasing graph
     */
    void releaseGraph(GraphDescription& gd, const UserInfo& user_info);
  
    /** Broadcasts registry information to network if UDPBroadcaster is set.
     *
     * @sa sendUDPBroadcaster
     */
    void sendIServerRegistry();

protected: // session sharing methods
  /* This finds the 'best' overlap of two graphs. Best overlap graph means in this case this graph
   * which shares the most nodes from both graphs. This method returns
   */
  pair<GraphDescription*, int> shareGraphs(GraphDescription& r_graph, GraphDescription& o_graph);


  int  valueGraph(const GraphDescription& o, const GraphDescription& q);
  void shareSubGraphs(list<GraphDescription* >& graph_list,
                    GraphDescription& o_graph,
                    GraphDescription& r_graph,
                    GraphDescription& q_graph);

  EdgeDescription::OverlapStatus overlapPartialOutput(
          const NodeDescription& nr_out, const EdgeDescription& nr_edge,
          const NodeDescription& nq_out, const EdgeDescription& nq_edge);

  EdgeDescription::OverlapStatus overlapPartialCopy(
          const NodeDescription& nr_out, const EdgeDescription& nr_edge,
          const NodeDescription& nq_out, const EdgeDescription& nq_edge);

  EdgeDescription::OverlapStatus overlapComplete(
          const NodeDescription& nr_out, const EdgeDescription& nr_edge, const NodeDescription& nr_in,
          const NodeDescription& nq_out, const EdgeDescription& nq_edge, const NodeDescription& nq_in );

  void mapSharedGraph(GraphDescription& query, GraphDescription& overlap, GraphDescription& running, const UserInfo&, const vector<NodeDescription::SharingType>& )
    throw (const RegistryException& );

protected:
   /** Run method of ServerRegistry.
     *
     * While running checks for timeout transactions or node reservations.
     */
    void run();

    /** Announces Registry.
     * 
     * Checks if we have a RegistryMemento for given Registry in our memento list.
     * If we find one, we set it in Registry.
     */
    virtual void announceRegistry(const RegistryID& reg_id);

  private:
    /** Checks password in given UserInfo.
     *
     * @param info UserInfo contains password to check
     * @param throw_exception if true, throw exception if password check fails, otherwise return false
     * @return true if password check succeeds, false otherwise
     */
    bool securityChecks(const UserInfo& info, bool throw_exception = true)
      throw(const RegistryException&);

    /** Updates transaction reservation time.
     *
     * If transaction reservation time drops below 0, closes transaction.
     */
    void checkTransactionLock();

    /** Updates node reservation time.
     *
     * If node reservation time drops below 0, release Node.
     */
    void checkNodeReservation();

    /** Request Nodes given threw list of Registry Responses.
     *
     * @param request_list list of Registry Responses
     * @param user_info UserInfo specifying user requesting node
     * @param nd NodeDescription describing the node
     * @return requested node
     */
    INode* requestNode(const list<Response>& request_list, const UserInfo& user_info, NodeDescription& nd)
      throw (const RegistryException& );

    /** Changes a nodes state to CONSTRUCTED.
     *
     * @param ref NMMReference to node
     */
    void prepareNodeForDestruction(const NMMReference& ref);

    enum SharingRule {LEFT_SIDE = 0, RIGHT_SIDE };

    /** Sets sharing type on nodes of given GraphDescription.
     *
     * @param gd GraphDescription wiht nodes to set sharing type
     * @param sharing_types vector specifying sharing type for nodes in graph description
     * @param rule SharingRule specifying if GraphDescription is master or not
     */
    void setSharingType(GraphDescription& gd, const vector<NodeDescription::SharingType>& sharing_types, SharingRule rule);


    /** Writes RegistryMementos of Registries in given list to given OValueStream.
     *
     * @param ov OValueStream to write to
     * @param reg_list list of registries to write to OValueStream
     */
    void writeToStream(OValueStream& ov, list<RegistryID> reg_list);

    /** Checks if there is a RegistryMemento for given Registry.
     *
     * If we found one, set memento for this Registry.
     *
     * @param reg_id Registry to check for RegistryMemento
     */
    void setMementoObject(const RegistryID& reg_id);

    /** Calls setMementoObject for every Registry in this list and for every of its child Registries.
     *
     * @param reg_id_list list of registries to set memento
     */
    void setMementoObject(list<RegistryID> reg_id_list);

  private:
    /** This struct stores all information about a reserved nodes
     */
    struct Reservation {
      /** Default constructor.
       */
      Reservation() : reference(0) {}
      /** Constructor.
       *
       * @param _reg_id RegistryID of registry that manages the node
       * @param _node_id ObjectID of reserved node
       * @param _user_info UserInfo of user that has done the reservation
       * @param _reservation_time leasing time for the node until it is released
       * @param ref NMMReference of the Node, if it is allocated in this address space
       */
      Reservation(const RegistryID _reg_id, const ObjectID& _node_id, const UserInfo& _user_info, long _reservation_time, const NMMReference& ref = 0)
        : reg_id(_reg_id), node_id(_node_id), user_info(_user_info), reservation_time(_reservation_time), reference(ref)
      { }
      RegistryID    reg_id;                                ///< stores the RegistryID that manages the node
      ObjectID      node_id;                               ///< stores the ObjectID of the reserved nodes
      UserInfo      user_info;                             ///< stores the UserInfo of the application that request the node
      long          reservation_time;                      ///< stores the leasing time for the node until it is released
      NMMReference  reference;                             ///< stores the NMMReference of the Node, if it is allocated in this address space
    };

    /** This struct stores all information about an enabled access control
     */
    struct TransactionLock {
      /** Constructor.
       */
      TransactionLock() : is_locked(false) {}
      UserInfo user_info;                                  ///< The UserInfo of the application which enables the access control
      long     reservation_time;                           ///< The leasing time until the access control is disabled
      bool     is_locked;                                  ///< Is true if access control is enabled
    };

    struct SessionStruct {
      /** Constructor.
       */
      SessionStruct() : session(0), i_session(0) {}
      /** Destructor.
       */
      ~SessionStruct() { delete i_session; delete session;}
      Session*     session;     ///< Session
      ISession*    i_session;   ///< interfaace to Session
      UserInfo     user_info;   ///< user initiating the session
    };

  private: //typedefs
    typedef pair<UserInfo, long> TransactionLock_t;

    typedef map<const ProxyObject*, NMMObject* >::iterator ObjectIt_t;
    typedef map<const ProxyObject*, NMMObject* >::const_iterator CObjectIt_t;

    typedef map<const ProxyObject*, SessionStruct>::iterator SessionIt_t;
    typedef map<const ProxyObject*, SessionStruct>::const_iterator CSessionIt_t;

  private: //attributes
    bool                    __is_running;                  ///< Threads running variable
    TransactionLock         __transaction_lock;            ///< Transaction locks
    list<Reservation>       __reservation_list;            ///< Reserved Nodes

    long                    __default_reservation_time;    ///< Default time for a servation in seconds
    long                    __default_transaction_time;    ///< Default time for a locked transaction in seconds
    long                    __time_interval;               ///< The interval in which the reservation is checked

    ThreadMutex             __member_mutex;                ///< Mutex to sync the internal thread with external method call
    ThreadMutex             __thread_mutex;                ///< Mutex for the __thread_condition
    ThreadCondition         __thread_condition;            ///< Used to wait a specific time in the thread's run method

    IRegistry_var           __local_registry;              ///< Methods are forwarded to this Registry-Interface

    map<const ProxyObject*, NMMObject*>    __object_map;   ///< stores allocated and reserved objects
    map<const ProxyObject*, SessionStruct> __session_list; ///< Stores the session available on this system

    bool                    __security_enabled;            ///< ture if security check should be done
   
    list<RegistryMemento>   __memento_list;                ///< list of not set RegistryMementos
    UDPBroadcaster*         __broadcaster;                 ///< broadcasts registry information
    int                     __port;                        ///< port registry listens on 

    string                  __synchronizer_name;           ///< Name of default synchronizer class to use

  };

  inline void ServerRegistry::registerPlugin(Plugin*) {}

  inline void ServerRegistry::unregisterPlugin(const Plugin*) {}

  inline void ServerRegistry::setMemento(const RegistryMemento&) {}

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

#endif
