/* 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_PROXY_OBJECT_HPP
#define NMM_PROXY_OBJECT_HPP NMM_PROXY_OBJECT_HPP

#include "nmm/nmm_prefix.hpp"

#include "NMMObject.hpp"
#include "BindingException.hpp"

#include "visibility.hpp"

namespace NMM {
  
  class OutOfBandChannel;
  
  /** A proxy object is used by an aplication to communicate with an 
   * actual object.  The proxy object can be used directly (by sending
   * events to it) or through interfaces that can be requested from
   * it.  It communicates with its partner object through an
   * %OutOfBandCommunicationChannel using an
   * %OutOfBandCompositeStrategy.  It also manages the supported
   * interfaces and available addresses of its partner object. If the
   * partner object is local to the proxy object, the proxy object can
   * communicate with it directly by bypassing the communication
   * channel. This local optimisation is used by default, unless it is
   * explicitly disabled.
   *
   * @see OutOfBandCommunicationChannel OutOfBandCompositeStrategy
   */
  class DLLEXPORT ProxyObject
    : public NMMObject,
      public NamedObject
  {
    friend class Interface;
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Constructs an unconnected proxy object.
     *
     * The proxy object can be connected to a partner object by calling connectToObject.
     *
     * @param auto_delete true if ProxyObject should delete itself after relase of last interface pointing to it
     */
    ProxyObject(bool auto_delete = false);
    
    /** Constructs a proxy object using the connection addresses inside the given interface.
     *
     * @param _interface interface which connection addresses should be used 
     * @param auto_delete true if ProxyObject shuld delete itself after relase of last interface pointing to it
     */
    ProxyObject(const Interface& _interface, bool auto_delete = false);
    
    /** Destructor.
     */
    virtual ~ProxyObject();
    //@}

    /** @name Interface methods
     */
    //@{
    /** Returns all Interfaces supported by this object.
     *
     * @return list of interfaces supported by this object
     */
    virtual const list<Interface*>& getInterfaceList()   const;

    /** Returns all Interfaces supported by this object.
     *
     * @return list of interfaces supported by this object
     */
    virtual       list<Interface*>& getInterfaceList()        ;
    //@}

    /** @name Event Processing
     */
    //@{
    /** Sends events to the NMMObject.
     *
     * @param event to be sent
     * @param event_sender sender of event
     */
    virtual Result receiveEvent(Event&  event, const IEventSender* event_sender = 0 );
    
    /** Sends reliable configuration events to the NMMObject.
     *
     * @param event event to send to NMMObject
     * @param event_sender sender of event
     */
    virtual Result receiveReliableEvent(Event& event, const IEventSender* event_sender = 0 ) const;
    
    /** Returns a prototype for the given Event key.
     *
     * @param key event key
     * @return pointer to Event
     */
    virtual Event* getEventPrototype(const string& key)  const;

    /** Returns an prototype event with a substring of an event key. 
     *
     * The caller of this method is responsible to delete this event.
     *
     * @param event_key event key
     * @return pointer to Event
     */
    virtual Event* matchEventPrototype(const string& event_key)  const;

    /** Returns the list of all supported event keys.
     *
     *  @return list of all supported event keys
     */
    virtual list<string> getEventList() const;
    //@}


    /** @name Connection methods
     */
    //@{
    /** Returns the current OutOfBandChannel.
     *
     * @return reference to current OutOfBandChannel
     */
    OutOfBandChannel& getOutOfBandChannel()       { return *__out_of_band_channel; }

    /** Returns the current OutOfBandChannel.
     *
     * @return reference to current OutOfBandChannel
     */
    const OutOfBandChannel& getOutOfBandChannel() const { return *__out_of_band_channel; }

    /** Connects to the NMMObject.
     *
     * @param address TSAddress of NMMObject
     */
    virtual void connectToObject(const TSAddress& address)
      throw (const BindingException&);

    /** Enforces a connection between the proxy and a distributed object.
     */
    virtual void establishBinding()
      throw (const BindingException&);

    /** Closes a connection to the NMMObject.
     */
    virtual void disconnectFromObject();
    
    /** Check whether local optimisation is allowed.
     *
     * @return true if local optimisation is allowed, false otherwise
     */
    bool isLocalOptimisationAllowed() const throw() {
      return __local_optimisation_allowed;
    }
    
    /** Sets flag specifying whether local optimisation is allowed.
     *
     * @param is_allowed flag specifying whether local optimisation is allowed
     */
    void setLocalOptimisationAllowed(bool is_allowed) throw() {
      __local_optimisation_allowed = is_allowed;
    }
    //@}

    /** @name Event Listener methods
     */
    //@{
    /** Method for remote event listener registration inherited from %NMMObject.
     *
     * This method does nothing. It is overloaded to prevent infinite recursion.
     */
    virtual void registerEventListener() {}
    
    /** Method for remote event listener registration inherited from %NMMObject.
     *
     * This method does nothing. It is overloaded to prevent infinite recursion.
     */
    virtual void registerSpecificEventListener(const string&) {}

    /** Method for remote event listener removal inherited from %NMMObject.
     *
     * This method does nothing. It is overloaded to prevent infinite recursion.
     */
    virtual void removeEventListener() {}
    
    /** Method for remote event listener removal inherited from %NMMObject.
     *
     * This method does nothing. It is overloaded to prevent infinite recursion.
     */
    virtual void removeSpecificEventListener(const string&) {}
    
    /** This method registers an event listener. 
     *
     * All registerd IEventSender receives the event.
     */
    virtual void registerEventListener(IEventReceiver*);

    /** Adds an TED-Object as EventListener. 
     * 
     * Incoming events with corresponding event-keys are forwarded to the TED-Object.
     *
     * @param event_key event key
     * @param edo TED-Object
     */
    virtual void registerEventListener(const string& event_key, EDObject* edo);

    /** Registers an event listener for a specific key.
     *
     * @param event_key event key
     * @param listener handler of event
     */
    virtual void registerEventListener(const string& event_key, IEventReceiver* listener);

    /** Removes an event listener.
     *
     * @param listener handler of event
     */
    virtual void removeEventListener(IEventReceiver* listener);

    /** Removes an event listener.
     *
     * @param event_key event key
     * @param listener handler of event
     */
    virtual void removeEventListener(const string& event_key, IEventReceiver* listener);

    /** Removes a certain TEDObject.
     * 
     * @param edo handler of event
     */
    virtual void removeEventListener(EDObject* edo);
    //@}

    /** @name Additional information
     */
    //@{
    /** Returns the default address which can be used to connect this node with strategy type strategy_type.
     *
     * @param strategy_type strategy type to search for
     */
    virtual TSAddress* getDefaultAddress(const string& strategy_type) const;

    /** Returns the address of this object as NMMReference.
     *
     * @return NMMReference to this object
     */
    virtual const NMMReference& getReference()                  const  { return __parent_reference; }

    /** Returns the address of this object as NMMReference.
     *
     * @return NMMReference to this object
     */
    virtual const NMMReference& getParentReference()            const  { return __parent_reference; }
    //@}

    /** Returns an interface to the registry.
     *
     * Returns an interface to the registry which created the NMMObject this ProxyObject references to.
     * 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.
     *
     * This method sets the interface to the registry from which the NMMObject this ProxyObject references to 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);

  protected:
    /** Updates the list of all interfaces.
     */
    void    updateInterfaces();
    
    /** Returns the list of all interfaces.
     *
     * This method gets the list of all interfaces supported by the NMMObject referenced by this ProxyObject.
     * 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 Result getInterfaceList(SequenceValue<Interface>& interface_list) const;

    /** Requests 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;

    /** Returns the user info.
     *
     * @param user_info variable for the user info
     * @return SUCCESS
     */
    virtual Result getUserInfo(UserInfo& user_info) const;
    
    /** Increases interface counter.
     *
     * This method gets called whenever an interface is created referencing this ProxyObject.
     */
    void increaseInterfaceCounter()      {++__interface_counter;}
    
     /** Decreases interface counter.
     *
     * This method gets called whenever an interface referencing this ProxyObject is deleted.
     * If interface reference counter drops to 0 or below, this ProxyObject deletes itself.
     */
    void decreaseInterfaceCounter()      {--__interface_counter; if ( (__interface_counter <= 0) && __auto_delete) delete this;}
    
  private:
    /** Register internal events at the event dispatcher.
     */
    void registerEvents();
    
    /** Send an IObjectAcceptor::connectToObject event if this %ProxyObject is not yet connected.
     */
    void sendConnectToObject()
      throw(const BindingException&);
    
    /** Listener for IObjectAcceptor::connectToObject.  This method is
     * invoked after sending an IObjectAcceptor::connectToObject event
     * through this %ProxyObject.  It updates the list of interfaces.
     *
     * @param reference reference to object to connect to
     */
    void connectToObject(const NMMReference& reference);
    
    /** Listener for IObjectAcceptor::connectToService.  
     *
     * This method  is invoked after sending an IObjectAcceptor::connectToService
     * event through this %ProxyObject.  It updates the %NMMReference
     * of the partner object and the list of interfaces.
     *
     * @param service_name not needed
     */
    void connectToService(const string& service_name);
    
  private:
    OutOfBandChannel*     __out_of_band_channel;          ///< stores the OutOfBandChannel
    mutable NMMReference  __parent_reference;             ///< Stores the NMMReference of the parent
    mutable bool          __is_connected;                 ///< Is true if the ProxyObject is connected
    int                   __interface_counter;            ///< Counter to interfaces that use this ProxyObject
    bool                  __auto_delete;                  ///< If true, the ProxyObject deletes themself, if no Interfaces point to it
    
    bool                  __local_optimisation_allowed;   ///< Local optimisation is allowed
    mutable bool          __is_local;                     ///< Partner object is local
    mutable NMMObject*    __partner_object;               ///< Pointer to the partner object if it is local
  };

}

#include "nmm/nmm_suffix.hpp"

#endif
