/* 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_STATICSERVERREGISTRY_HPP
#define NMM_STATICSERVERREGISTRY_HPP

#include "nmm/nmm_prefix.hpp"

#include "Registry.hpp"
#include "IServerRegistryImpl.hpp"
#include "nmm/utils/thread/ThreadedObject.hpp"
#include "nmm/comm/netstrategy/UDPBroadcaster.hpp"
#include "Session.hpp"
#include "EdgeDescription.hpp"
#include <list>

#include "visibility.hpp"

namespace NMM {
  class GraphDescription;
  class Synchronizer;
  class Session;
  class ISession;

/** \class StaticServerRegistry
  */
class DLLEXPORT StaticServerRegistry : public Registry, public IServerRegistryImpl {
public:

/** Constructor
  */
  StaticServerRegistry(bool security_check = true);

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

  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.
  */
//@{
/** 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.
  */
  virtual list<Response >  initRequest(NodeDescription& node_desc) const;

/** 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.
  * \sa reserveNode()
  * \sa instantiateNode()
  */
  virtual INode* requestNode(const RegistryID& reg, const ObjectID& node_id, const UserInfo& info, NodeDescription& nd)
    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 The RegistryID of the Registry which manages the node.
  * \param node_id The ObjectID of the Node that is released.
  * \param 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);


  virtual void releaseSharedNode(const INode&);

/** 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.
  */
  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
  */
  virtual INode* instantiateNode(const RegistryID& reg_id, const FactoryTicket& ticket, const UserInfo&)
    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&);
//@}

/** @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.
  */
  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.
  * \param node_id The ObjectID of the node.
  */
  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.
  */
  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.
  */
  virtual list<NodeInfo> getNodeInfoList(const RegistryID& reg_id) const;

/** This method returns the complete NodeDescription of aspecifc node.
  * \param reg_id The RegistryID of the Registry that should be searched.
  * \param node_id The ObjectID of the 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.
  */
  virtual list<NodeDescription> getNodeDescriptionList(const RegistryID&);

/** 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.
  */
  virtual NodeFactoryID getNodeFactoryID(const RegistryID& reg_id, const ObjectID& node_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 specifc user.
 */
  virtual void resetSpecificInternalReservation(const UserInfo& user_info);
 
  RegistryMemento getMemento();

  void setMemento(const RegistryMemento& rm);
  virtual void registerPlugin(NMM::Plugin *) {}
  virtual void unregisterPlugin(const NMM::Plugin *) {}
//@}
protected: //IServerRegistry interface
  enum SharingRule {LEFT_SIDE = 0, RIGHT_SIDE };

/** @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 timout 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 time_out 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.
  */
  virtual nmm_uint32 refreshNodeLeasing(const RegistryID& reg_id, const ObjectID& node_id, const UserInfo& );

/** This method refreshes the enabled access control.
  */
  virtual nmm_uint32  refreshTransaction(const UserInfo&);
//@}

  virtual Interface* requestNMMObject(const ObjectDescription& od)
    throw(const Exception&);

  virtual void releaseNMMObject(const Interface& i_face)
    throw(const Exception&);

  void setSharingType(GraphDescription& gd, const vector<NodeDescription::SharingType>& sharing_types, SharingRule rule);

  /* Begin > Session sharing interface */
  ISession* initSession(GraphDescription& gd, const UserInfo& u_info, const ISession* given_session);

  void releaseSession(const ISession& session, const UserInfo& );

  void searchSharedGraphs(GraphDescription& query_graph, GraphDescription&);

  void requestGraph(GraphDescription& gd, const UserInfo& user_info);

  ISession* requestSharedGraph(GraphDescription& gd, const UserInfo& user_info)throw (const NMM::RegistryException&);

  void releaseGraph(GraphDescription& , const UserInfo& );

  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& user_info, const vector<NodeDescription::SharingType>& sharing_types)
  throw (const RegistryException& );

protected:
  void run();

private:
  bool securityChecks(const UserInfo& info, bool throw_exception = true)
    throw(const RegistryException&);

  void checkTransactionLock();
  void checkNodeReservation();

  INode* requestNode(const list<Response>& request_list, const UserInfo& user_info, NodeDescription& nd)
    throw (const RegistryException& );

/** changes a nodes state to CONSTRUCTED
  */
  void prepareNodeForDestruction(const NMMReference& ref);

private:
/** This struct stores all information about a reserved nodes
  */
  struct Reservation {
    Reservation() : reference(0) {}
    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 {
    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 {
    SessionStruct() : session(0), i_session(0) {}
    ~SessionStruct() { delete i_session; delete session;}
    Session*     session;
    ISession*    i_session;
    UserInfo     user_info;
  };

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;

  void createInternalNodeMap( );

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;

   /** The InternalStruct stores all information need to manage a Node
    */
  struct InternalStruct {
    InternalStruct() : instance_count(0), max_instance_count(-1) {}
    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
  };
  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
  UDPBroadcaster* __broadcaster;
  int __port;

  /** Store the maximum number of node instantiations we want to try in createInternalNodeMap()
    */
  static const unsigned int MAX_INSTANTIATIONS = 50;
};



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

#endif


