/* 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_NMMOBJECT_HPP
#define NMM_NMMOBJECT_HPP NMM_NMMOBJECT_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/comm/messaging/IEventReceiver.hpp"
#include "nmm/comm/messaging/EventDispatcher.hpp"

#include "nmm/comm/serialize/SequenceValue.hpp"

#include "NMMReference.hpp"
#include "InterfaceNotFoundException.hpp"

#include "visibility.hpp"

namespace NMM {

  class Interface;
  class UserInfo;

  class TSAddress;
  class TSDescription;

  class ITransportStrategy;

  class ICSEventReceiver;
  class OutOfBandCompositeStrategy;
  class ReferenceManager;

  /** An NMM object.
   * An NMM object is an object which is accessible from the network through interfaces.
   * This class is the base class of all NMM objects and of the ProxyObject class, which is used
   * for accessing NMM objects through interfaces.
   *
   * Every NMM object is an event receiver which processes out-of-band events by using an event
   * dispatcher. Subclasses of NMMObject register their event handling methods at the event reveiver.
   * The preferred way to do this is to define interfaces and inherit from the corresponding interface
   * implementation classes.
   *
   * Each NMM object manages a list of interfaces it implements. This list of interfaces is requested
   * and cached by proxy objects which expose the interfaces of the object to the application.
   *
   * The NMMObject class also provides a remote-accessible interface for network-transparent registration
   * of distributed event listeners.
   */
  class DLLEXPORT NMMObject
    : public IEventReceiver
  {
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Create a new NMM object.
     */
    NMMObject();

    /** Virtual destructor.
     */
    virtual ~NMMObject();

    /** Copy constructor.
     *
     * @param object The original object
     */
    NMMObject(const NMMObject& object);
    //@}

    /** @name Event Processing
     */
    //@{
    /** Receive an event.
     *
     * This method is called to send an event to this object.
     *
     * @param event The event to send to this object
     * @param sender The object which sent the event
     * @return @c SUCCESS if the event was successfully handled, or @c FAILURE otherwise
     */
    virtual Result receiveEvent(Event& event, const IEventSender* sender = 0);

    /** Get an event prototype.
     * This method returns a prototype event for a given event key. The caller of this method
     * is responsible for deleting the returned event.
     *
     * @param event_key Event key
     * @return Event prototype for this event
     */
    virtual Event* getEventPrototype(const string& event_key) const;

    /** Returns an Event prototype with substring s in event key.
     *
     * Returns an Event prototype that has a substring s in event key, NULL if no such Event exists.
     *
     * @param s event key substring to search for
     * @return Event prototype with substring s in event key
     */
    virtual Event* matchEventPrototype(const string& s) const;

    /** Get the event dispatcher of this object.
     *
     * @return A reference to the event dispatcher of this object
     */
    inline        EventDispatcher& getEventDispatcher()                { return __event_dispatcher; }

    /** Get the list of all events.
     * This method returns a string list containing the event keys of all events which are registered
     * at the event dispatcher of this object.
     *
     * @return A list of all events registered at this node
     */
    virtual list<string> getEventList() const;
    //@}

    /** @name Listener Notification
     */
    //@{
    /** Register an event listener.
     * This method registers an event listener. The listener receives all events received by this object.
     *
     * @param listener IEventReceiver interface of the listener
     */
    virtual void registerEventListener(IEventReceiver* listener);

    /** Register an event listener for a specific event.
     * This method registers an event listener for the event with the given event key.
     * The listener receives all events with the given event key received by this object.
     *
     * This method directly registers an event handler at the event dispatcher.
     *
     * @param event_key Event key
     * @param handler The handler to listen for the event
     */
    virtual void registerEventListener(const string& event_key, EDObject* handler);

    /** Register an event listener for a specific event.
     * This method registers an event listener for the event with the given event key.
     * The listener receives all events with the given event key received by this object.
     *
     * @param event_key Event key
     * @param listener IEventReceiver interface of the listener
     */
    virtual void registerEventListener(const string& event_key, IEventReceiver* listener);

    /** Remove an event listener.
     * This method removes an event listener which has been registered for all events.
     *
     * @param listener IEventReceiver interface of the listener
     */
    virtual void removeEventListener(IEventReceiver* listener);

    /** Remove an event listener for a specific event.
     * This method removes an event listener which has been registered for a specific events.
     *
     * @param event_key Event key
     * @param listener IEventReceiver interface of the listener
     */
    virtual void removeEventListener(const string& event_key, IEventReceiver* listener);

    /** Remove an event listener for a specific event.
     * This method removes an event listener which has been registered for a specific events
     * by directly registering an event handler at the event dispatcher.
     * The method guarantees that it looks like an accident.
     *
     * @param handler The handler to listen for the event
     */
    virtual void removeEventListener(EDObject* handler);
    //@}

    /** @name Interfaces
     */
    //@{
    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or 0 if this interface
     * is not supported by this object. The use of this method is:
     * @code
     * NMMObject my_object;
     * INode* i_node = my_object.getInterface<INode>();
     * if (i_node) {
     *   cout << "INode Interface supported" << endl;
     * }
     * else {
     *   cout << "INode Interface not supported" << endl;
     * }
     * @endcode
     * The returned interface must be deleted by the caller.
     *
     * @return The requested interface or 0
     */
    template <typename T>
    T* getInterface() {
      T::used();
      T* tmp_interface = getOwnInterface<T>();
      if (tmp_interface) {
        return dynamic_cast<T*>(tmp_interface->copy());
      } else {
        return 0;
      }
    }

    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or 0 if this interface
     * is not supported by this object. The use of this method is:
     * @code
     * NMMObject my_object;
     * INode* i_node = my_object.getInterface<INode>();
     * if (i_node) {
     *   cout << "INode Interface supported" << endl;
     * }
     * else {
     *   cout << "INode Interface not supported" << endl;
     * }
     * @endcode
     * The returned interface must be deleted by the caller.
     *
     * @return The requested interface or 0
     */
    template <typename T>
    const T* getInterface() const {
      T::used();
      const T* tmp_interface = getOwnInterface<T>();
      if (tmp_interface) {
        return dynamic_cast<T*>(tmp_interface->copy());
      }
      return 0;
    }

    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or throws an exception
     * if this interface is not supported by this object. The use of this method is:
     * @code
     * NMMObject my_object;
     * try {
     *   INode* i_node = my_object.getInterface<INode>();
     *   cout << "INode Interface supported" << endl;
     * }
     * catch(InterfaceNotFoundException& e) {
     *   cout << "INode Interface not supported" << endl;
     * }
     * @endcode
     * The returned interface must be deleted by the caller.
     *
     * @return The requested interface or 0
     */
    template <typename T>
    const T* getCheckedInterface() const throw (const InterfaceNotFoundException&)
    {
      T::used();
      const T* tmp_interface = getOwnInterface<T>();
      if (tmp_interface) {
            return dynamic_cast<T*>(tmp_interface->copy());
      }
      throw InterfaceNotFoundException();
    }

    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or throws an exception
     * if this interface is not supported by this object. The use of this method is:
     * @code
     * NMMObject my_object;
     * try {
     *   INode* i_node = my_object.getInterface<INode>();
     *   cout << "INode Interface supported" << endl;
     * }
     * catch(InterfaceNotFoundException& e) {
     *   cout << "INode Interface not supported" << endl;
     * }
     * @endcode
     * The returned interface must be deleted by the caller.
     *
     * @return The requested interface or 0
     */
    template <typename T>
    T* getCheckedInterface() throw (const InterfaceNotFoundException&)
    {
      T::used();
      T* tmp_interface = getOwnInterface<T>();
      if (tmp_interface) {
            return dynamic_cast<T*>(tmp_interface->copy());
      }
      throw InterfaceNotFoundException();
    }


    /** Get list of all interfaces.
     * This method gets the list of all interfaces supported by this object.
     * The interface objects in the list must not be deleted by the caller.
     * All interface pointers are valid until this object is destroyed.
     *
     * @return The list of all interfaces
     */
    virtual const list<Interface*>& getInterfaceList()  const     {return __interface_list;}

    /** Get list of all interfaces.
     * This method gets the list of all interfaces supported by this object.
     * The interface objects in the list must not be deleted by the caller.
     * All interface pointers are valid until this object is destroyed.
     *
     * @return The list of all interfaces
     */
    virtual       list<Interface*>& getInterfaceList()            {return __interface_list;}

    /** Add an interface to this object.
     * This method adds an interface to the list of interfaces supported by this object.
     *
     * @param iface An interface
     */
    inline  void   addInterface(Interface* iface)                 { if (iface) __interface_list.push_back(iface); }
    //@}

    /** @name Binding Types
     */
    //@{
    /** Check if this object is local to the given interface.
     * This method checks if this object and the object referenced by the given interface are local to each other.
     * Two objects are local to each other if they are in the same address space.
     *
     * @param iface Interface which identifies the other object
     * @return @c true if both objects are local to each other, @c false otherwise
     */
    virtual bool isLocalTo(const Interface& iface);

    /** Check if this object is on teh same system as the given interface.
     * This method checks if this object and the object referenced by the given interface are on the same system.
     * Two objects are local to each other if they are within the address spaces of processes running on the same host.
     *
     * @param iface Interface which identifies the other object
     * @return @c true if both objects are on the same system, @c false otherwise
     */
    virtual bool isSystemTo(const Interface& iface);

    /** Check if this object is neither local to nor on the same system as the given interface.
     * This method checks if this object and the object referenced by the given interface
     * are neither local to each other nor on the same system.
     * Two objects are neither local to each other nor on the same system if they are not within the address spaces of
     * processes running on the same host.
     *
     * @param iface Interface which identifies the other object
     * @return @c true if both objects are on the same system, @c false otherwise
     */
    virtual bool isNetworkTo(const Interface& iface);
    //@}

    /** @name Addressing and Connection Parameters
     */
    //@{
    /** Get the default address for the given strategy type.
     * This method returns the default address which is used when connecting to this object using the given type of
     * transport strategy.
     * If it is not possible to connect to this object using the given type of transport strategy, then this method
     * returns a null pointer.
     * The caller is responsible for deleting the returned object.
     *
     * Subclasses may reimplement this method to specify restrictions about which transport strategies can be used to
     * connect to this object.
     *
     * @param strategy_type Type of transport strategy
     * @return The default address or 0
     */
    virtual TSAddress* getDefaultAddress(const string& strategy_type) const;

    /** Get supported transport strategies.
     * This method returns the list of transport strategies supported by this object.
     *
     * @param strategy_list Returns the list of supported transport strategies
     * @return SUCCESS
     */
    virtual Result getSupportedStrategies(list<TSDescription>& strategy_list) const;

    /** Get the NMM reference of this NMM object.
     * This method returns an NMM reference which uniquely identifies this object within its address space.
     *
     * @return NMM reference of this object
     */
    virtual const NMMReference& getReference()                  const  { getReference(__reference); return __reference; }

    /** Get an interface to the registry which created this object.
     * If this object was requested from a registry, then this method returns an interface to the registry.
     * Otherwise, a null pointer is returned.
     * The returned object must not be deleted by the caller.
     *
     * @return An interface to the registry or 0
     */
    virtual Interface* getRegistry();

    /** Sets the interface to the registry which created this object.
     * This method sets the interface to the registry from which this object was requested or a null pointer
     * if this object was not requested from a registry.
     *
     * @param registry An interface to the registry or 0
     */
    virtual void setRegistry(const Interface& registry);
    //@}

    /** @name Connection Management
     */
    //@{
    /** Lock the list of connected transport strategies.
     */
    void lockStrategyList() const throw() {
      __internal_mutex.lock();
    }

    /** Unlock the list of connected transport strategies.
     */
    void unlockStrategyList() const throw() {
      __internal_mutex.unlock();
    }

    /** Get the list of connected transport strategies.
     * This method returns a copy of the list of all transport strategies which are currently connected to
     * this object. The transport strategy objects in the list may not be deleted by the caller.
     *
     * @return List of connected transport strategies
     */
    const list<OutOfBandCompositeStrategy*> getStrategyList() const throw() {
      return __strategy_list;
    }

    /** Add a connected TransportStrategy to this object.
     * This method adds the given transport strategy to the list of connected transport strategies.
     *
     * @param strategy A connected transport strategy
     */
    void addTransportStrategy(OutOfBandCompositeStrategy* strategy);

    /** Remove a connected TransportStrategy from this object.
     * This method adds the given transport strategy from the list of connected transport strategies.
     *
     * @param strategy A connected transport strategy
     */
    void removeTransportStrategy(OutOfBandCompositeStrategy* strategy);
    //@}

    /** Set the reference manager.
     * This method replaces the real reference manager by a fake one. Used for doing evil.
     *
     * @deprecated This is a test method which should not be used
     *
     * @param reference_manager A pointer to the reference manager
     */
    void setReferenceManager(ReferenceManager* reference_manager);

  protected:
    /** Get the event dispatcher of this object.
     *
     * @return A reference to the event dispatcher of this object
     */
    inline        EventDispatcher& getEventDispatcher()        const  { return __event_dispatcher; }

    /** Get the current event sender.
     * This method returns an IEventSender interface to the object which sent the event which is
     * currently being handled. It can be called by event handling methods.
     * The returned pointer must not be deleted by the caller.
     *
     * @return The current event sender
     */
    inline  const IEventSender* getCurrentSender()             const  { return __current_sender;}

    /** Get the current event sender.
     * This method returns an IEventSender interface to the object which sent the event which is
     * currently being handled. It can be called by event handling methods.
     * The returned pointer must not be deleted by the caller.
     *
     * @return The current event sender
     */
    inline        IEventSender* getCurrentSender()                    { return __current_sender;}

    /** Set the current event sender.
     * This method is called by receiveEvent to store the sender of an event.
     *
     * @param current_sender The current event sender
     */
    inline void setCurrentSender(IEventSender* current_sender)        { __current_sender = current_sender; }

    /** Set the current event sender.
     * This method is called by receiveEvent to store the sender of an event.
     *
     * @param current_sender The current event sender
     */
    inline void setCurrentSender(IEventSender* current_sender) const  { __current_sender = current_sender; }

    /** Remove an event handler.
     * This method removes an event handler from the event dispatcher and returns the handler object
     * previously registered, if any, or a null pointer, otherwise.
     *
     * @param key Event key
     * @return The handler previously registered or 0
     */
    inline EDObject* removeEvent(const string& key)                   { return __event_dispatcher.removeEvent(key); }

    /** Register an event handler.
     * This method registers an event handler at the event dispatcher.
     *
     * @param key Event key
     * @param edo Event handler
     */
    inline void registerEvent(const string& key, EDObject* edo)       { __event_dispatcher.registerEvent(key, edo); }

    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or 0 if this interface
     * is not supported by this object.
     *
     * The returned interface must not be deleted by the caller.
     * The pointer stays valid until this object is deleted.
     *
     * This method is intended for use within subclasses because it has less overhead than getInterface().
     * Interface objects returned by this method should never be passed directly to other objects
     * (however, copying is allowed).
     *
     * @return The requested interface or 0
     */
    template <typename T>
    T* getOwnInterface();

    /** Get a new interface to this NMM object.
     * This template method returns a pointer to the Interface of type T or 0 if this interface
     * is not supported by this object.
     *
     * The returned interface must not be deleted by the caller.
     * The pointer stays valid until this object is deleted.
     *
     * This method is intended for use within subclasses because it has less overhead than getInterface().
     * Interface objects returned by this method should never be passed directly to other objects
     * (however, copying is allowed).
     *
     * @return The requested interface or 0
     */
    template <typename T>
    const T* getOwnInterface() const;

  protected: //event methods
    /** Create an NMM reference to this object.
     *
     * @param reference Returns the NMM reference
     * @return SUCCESS
     */
    virtual Result getReference(NMMReference& reference) const;

    /** Get a list of all interface names.
     *
     * @return List of interface names of all supported interfaces
     *
     * @deprecated This method is obsolete
     */
    virtual Result getInterfaceList(list<string>& interface_list) const;

    /** Get the user info.
     *
     * @param user_info Returns the user info
     * @return SUCCESS
     */
    virtual Result getUserInfo(UserInfo& user_info) const;

    /** Get a list of all transport strategy names
     *
     * @param strategy_list variable for list of interface names of all supported transport strategies
     * @return SUCCCESS
     *
     * @deprecated This method is obsolete
     */
    virtual Result getStrategyList(list<string>& strategy_list) const;

    /** Get a list of all interfaces.
     *
     * @return List of all interfaces
     */
    SequenceValue<Interface> getInternalInterfaceList() const;

    /** Get the number of supported interfaces.
     *
     * @return The number of supported interfaces
     */
    nmm_uint32 getInterfaceSize();

    /** Get the interface with the given index.
     *
     * @param i Interface index
     * @return The requested interface
     */
    Interface* getInternalInterface( nmm_int32 i );

    /** Handle disconnection from this object.
     * This method is called internally by the proxy object to notify this NMM object that the proxy object
     * has disconnected. The proxy object decides whether to delete the transport strategy
     *
     * @param delete_strategy @c true to delete the transport strategy, @c false to not delete it
     */
    virtual void disconnectFromObject(const nmm_bool& delete_strategy);
  private:
    /** Register the current event sender as event listener.
     * This is an internal out-of-band event sent by the proxy object to register an event listener for
     * all events. The implementation registers the current event sender as an event listener so that all
     * events received by this object are sent back to the transport strategy from which this event was
     * received.
     */
    virtual void registerEventListener();

    /** Remove the current event sender as event listener.
     * This is an internal out-of-band event sent by the proxy object to remove an event listener for
     * all events. The implementation removes the current event sender as an event listener.
     */
    virtual void removeEventListener();

    /** Register the current event sender as event listener for a specific event.
     * This is an internal out-of-band event sent by the proxy object to register an event listener for
     * all events with the given event key. The implementation registers the current event sender as an
     * event listener so that all events received by this object are sent back to the transport strategy
     * from which this event was received.
     *
     * @param key Event key
     */
    virtual void registerSpecificEventListener(const string& key);

    /** Remove the current event sender as event listener for a specific event.
     * This is an internal out-of-band event sent by the proxy object to remove an event listener for
     * all events with the given event key.
     * The implementation removes the current event sender as an event listener.
     *
     * @param key Event key
     */
    virtual void removeSpecificEventListener(const string& key);

    /** Get the partner strategy.
     * This is an internal out-of-band event sent by the proxy object to determine the partner strategy
     * of the transport strategy it uses for communication with the NMM object. It returns the transport
     * strategy of the current event sender.
     *
     * @return The transport strategy from which the current event was received
     */
    virtual ITransportStrategy* getPartnerStrategy();

    /** Event listener adapter.
     * This class is an adapter with an IEventReceiver interface for an ICSEventReceiver interface.
     * It is used for registering a transport strategy as an event listener at an event dispatcher.
     * Events sent over a transport strategy must be passed to the ICSEventReceiver interface of
     * the transport strategy, but only IEventReceiver interfaces may be registered as listeners.
     */
    class ProxyListener : public IEventReceiver {
    public:
      /** Create an event listener adapter.
       *
       * @param receiver The ICSEventReceiver interface
       */
      ProxyListener(ICSEventReceiver* receiver) throw()
        : __receiver(receiver)
      {}

      /** Receive an event.
       * This method implements the IEventReceiver interface. The incoming event is passed
       * on to the ICSEventReceiver interface.
       *
       * @param event The event
       * @param sender The sender of the event
       * @return Result of the event
       */
      virtual Result receiveEvent(Event& event, const IEventSender* sender = 0);

    private:
      ICSEventReceiver* __receiver; ///< The ICSEventReceiver interface
    };

  private:
    /** Register internal events at the event dispatcher.
     */
    void registerEvents();

  private:
    list<Interface*>                   __interface_list;             ///< List of all interfaces
    mutable EventDispatcher            __event_dispatcher;           ///< Event dispatcher
    mutable IEventSender*              __current_sender;             ///< Sender of the event which is currently handled
    mutable NMMReference               __reference;                  ///< NMM reference of this object
    mutable ThreadMutex                __internal_mutex;             ///< Mutex for protecting the connected strategy list
    Interface*                         __i_registry;                 ///< Interface to registry which created this object
    int                                __ref_count;                  ///< Reference count

    /** Event listener adapter map.
     * This type is a map which associates event listener adapters with transport strategies.
     */
    typedef map<OutOfBandCompositeStrategy*, ProxyListener> ListenerMap;

    /** Constant iterator in an event listener adapter map.
     */
    typedef ListenerMap::const_iterator ListenerConstIterator;

    /** Iterator in a event listener adapter map.
     */
    typedef ListenerMap::iterator ListenerIterator;

    list<OutOfBandCompositeStrategy*>  __strategy_list;              ///< List of connected transport strategies
    ListenerMap                        __listener_map;               ///< Event listener adapter map
    ReferenceManager*                  __reference_manager;          ///< Fake reference manager
  };




  template <typename T>
  T* NMMObject::getOwnInterface() {
    T* iface = 0;
    for (list<Interface*>::iterator it = getInterfaceList().begin(); it != getInterfaceList().end(); ++it)
      {
        iface = dynamic_cast<T*>(*it);
        if (iface != 0)
          return iface;
      }
    return 0;
  }


  template <typename T>
  const T* NMMObject::getOwnInterface() const {
    const T* iface = 0;
    for (list<Interface*>::const_iterator it = getInterfaceList().begin(); it != getInterfaceList().end(); ++it)
      {
        iface = dynamic_cast<const T*>(*it);
        if (iface != 0)
          return iface;
      }
    return 0;
  }

}

#include "nmm/nmm_suffix.hpp"

#endif
