/* 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_UDP_STRATEGY_HPP
#define NMM_UDP_STRATEGY_HPP NMM_UDP_STRATEGY_HPP

#include "nmm/nmm_prefix.hpp"

#include "IPGeneric.hpp"
#include "IUDPControlImpl.hpp"
#include "IByteCounterImpl.hpp"

#include "UDPAddress.hpp"

#include "nmm/comm/TransportStrategy.hpp"
#include "nmm/comm/TSAddressOwner.hpp"
#include "nmm/comm/ISetValueStreamImpl.hpp"

#include "nmm/comm/messaging/ITSRequestReplyEventReceiver.hpp"
#include "nmm/comm/messaging/ITSRequestReplyEventSender.hpp"
#include "nmm/comm/messaging/ITSOneWayCEventReceiver.hpp"
#include "nmm/comm/messaging/ITSOneWayCEventSender.hpp"
#include "nmm/comm/messaging/ITSOneWayBufferReceiver.hpp"
#include "nmm/comm/messaging/ITSOneWayBufferSender.hpp"

#include "nmm/utils/net/ThreadedNetHandler.hpp"
#include "nmm/utils/net/SockDGram.hpp"

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

#include "visibility.hpp"

namespace NMM {

  class IValueStream;
  class OValueStream;

  class ONetStream;
  class INetStream;

  class Message;

  class IConnectionManager;

  /** UDP transport strategy.
   * This class implements a transport strategy which uses the User Datagram Protocol (UDP/IP).
   *
   * This transport strategy implements the following transport roles:
   *
   * - Out-of-band event receiver
   * - Out-of-band event sender
   * - Instream composite event receiver
   * - Instream composite event sender
   * - Instream buffer receiver
   * - Instream buffer sender
   *
   * The transport strategy is unreliable and does not support direct connection setup.
   *
   * This transport strategy uses value streams and network streams for serializing data for network
   * transmission. By default, MagicNumberIValueStream and MagicNumberOValueStream are used as value streams,
   * and PacketINetStream and PacketONetStream are used as network streams.
   * It is possible to set different stream classes using the ISetValueStream interface
   *
   * The IPControl and IUDPControl interfaces can be used to set protocol-specific options
   * and for getting statistics from the PacketINetStream and PacketONetStream.
   *
   * This transport strategy uses the SockDGram class to handle UDP sockets.
   * Event notification is performed using the NetReactor and a ThreadedNetHandler.
   *
   * @see UDPAddress, SockDGram, PacketINetStream, PacketONetStream, MagicNumberIValueStream, MagicNumberOValueStream
   */
  class DLLEXPORT UDPStrategy
    : public TransportStrategy,
      public IPGeneric,
      public IUDPControlImpl,
      public ITSRequestReplyEventReceiver,
      public ITSOneWayCEventReceiver,
      public ITSOneWayBufferReceiver,
      public ITSRequestReplyEventSender,
      public ITSOneWayCEventSender,
      public ITSOneWayBufferSender,
      public ThreadedNetHandler,
      public TSAddressOwner,
      public ISetValueStreamImpl,
      public IByteCounterImpl,
      protected ThreadedObject
  {
  private:
    /** UDP message type. */
    enum UDPMessage {
      EVENT_REQUEST                   = 0, ///< Out-of-band event request (Event)
      EVENT_RESPONSE                  = 1, ///< Out-of-band event reply (Event, Result)
      CEVENT_ONE_WAY                  = 2, ///< One-way Instream event (CEvent)
      BUFFER_ONE_WAY                  = 3  ///< One-way Instream buffer (CEvent)
    };
    
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Constructor.
     *
     * @param name name of this object
     */
    UDPStrategy(const string& name = "UDPStrategy") ;

    /** Virtual destructor
     */
    virtual ~UDPStrategy();

    /** Virtual copy constructor.
     *
     * @return A copy of this object which has the same type
     */
    virtual UDPStrategy* copy() const { return new UDPStrategy(); }

    /** Schedule destruction of this transport strategy.
     * This method schedules this transport strategy for destruction.
     */
    virtual void scheduleDestruction();
    //@}

    /** @name IPGeneric specifc methods
     */
    //@{
    /** Get the socket object.
     * This method returns a reference to the socket object which represents the socket.
     * It must be implemented in the subclass which actually owns the socket object.
     *
     * @return Reference to socket
     */
    virtual Sock& getSocket() {
      return getDatagramSocket();
    }

    /** Get the socket object.
     * This method returns a reference to the socket object which represents the socket.
     * It must be implemented in the subclass which actually owns the socket object.
     *
     * @return Reference to socket
     */
    virtual const Sock& getSocket() const {
      return getDatagramSocket();
    }

    /** Get the datagram socket object.
     * This method returns a reference to the socket object which represents the socket.
     * It must be implemented in the subclass which actually owns the socket object.
     *
     * @return Reference to socket
     */
    inline       SockDGram& getDatagramSocket()        { return __sock; }

    /** Get the datagram socket object.
     * This method returns a reference to the socket object which represents the socket.
     * It must be implemented in the subclass which actually owns the socket object.
     *
     * @return Reference to socket
     */
    inline const SockDGram& getDatagramSocket()  const { return __sock; }
    //@}

    /** @name Receiver Interface
     */
    //@{
    /** Receive CEvent one-way.
     *
     * This method passes a CEvent to this transport strategy without
     * waiting for a reply.
     *
     * The event is sent over the network, then this method returns immediately.
     *
     * @param cevent A composite event
     */
    virtual Result receiveOneWayCEvent(CEvent* cevent);

    /** Receive Buffer one-way.
     *
     * This method passes a Buffer to this transport strategy without
     * waiting for a reply.
     *
     * The buffer is sent over the network, then this method returns immediately.
     *
     * @param buffer A buffer
     */
    virtual Result receiveOneWayBuffer(Buffer* buffer);

    /** Receive Event one-way.
     *
     * This method passes an Event to this transport strategy without
     * waiting for a reply.
     *
     * The event request is sent over the network.
     * This method returns when a reply for this event has been received.
     *
     * @param event An event
     */
    virtual Result receiveRequestReplyEvent(Event& event);
    //@}

    /** @name Configure the Transport-Strategy
     */
    //@{
    /** Sets address and connect to peer.
     *
     * This method connects this %TransportStrategy with its peer.
     * The given peer address must be of type UDPAddress.
     *
     * @param address Address of peer to connect to
     */
    virtual void setAddress(const TSAddress& address)
      throw (const BindingException&);

    /** Handle a signal.
     * This method handles blocking and unblocking of the receiver based on the given signal.
     *
     * @param signal A signal
     */
    virtual void   receiveSignal(Signal signal);

    /** Returns the typename of this %TransportStrategy.
     *
     * @return typename of this %TransportStrategy
     */
    virtual string getStrategyType() const { return getStaticStrategyType(); }

    /** Set the network output stream.
     * This method sets the network output stream used by this transport strategy.
     *
     * @param stream Network output stream
     */
    virtual void setONetStream(const ONetStream& stream);

    /** Set the network input stream.
     * This method sets the network input stream used by this transport strategy.
     *
     * @param stream Network input stream
     */
    virtual void setINetStream(const INetStream& stream);

    /** Get the network input stream.
     * This method gets the network input stream used by this transport strategy.
     *
     * @return Network input stream
     */
    virtual INetStream& getINetStream()                     { return *__i_net_stream; }

    /** This method checks if the strategy is connected.
     *
     * @return true if the strategy is connected, false otherwise
     */
    virtual bool isConnected() const {
      return __is_running;
    }

    /** Get the network input stream.
     * This method gets the network input stream used by this transport strategy.
     *
     * @return Network input stream
     */
    virtual const INetStream& getINetStream()         const { return *__i_net_stream; }

    /** Get the network output stream.
     * This method gets the network output stream used by this transport strategy.
     *
     * @return Network output stream
     */
    virtual ONetStream& getONetStream()                     { return *__o_net_stream; }

    /** Get the network output stream.
     * This method gets the network output stream used by this transport strategy.
     *
     * @return Network output stream
     */
    virtual const ONetStream& getONetStream()         const { return *__o_net_stream; }

    /** Configure the %TransportStrategy for the given instream format.
     * This implementation does nothing because the instream format does not matter.
     *
     * @param format The instream format
     * @exception FormatNotSupportedException if configuration fails or the format is not supported
     */
    virtual void configure(const Format& format)
      throw(const FormatNotSupportedException&);

    /** Sends a reliable event to the partner %NMMObject.
     *
     * This method reliably sends an event to the partner %NMMObject. It is used for sending events
     * that configure a binding. The event is sent through the %TransportStrategy itself (if supported)
     * or through a reliable middleware-internal channel (%IConnectionManager interface)
     *
     * @param event The event that should be sent
     * @return result of event
     */
    virtual Result sendReliableEvent(Event& event) const;

    /** Send a reliable event to the partner %TransportStrategy.
     *
     * This method reliably sends an event to the partner %TransportStrategy. It is used for sending events
     * that configure a binding. The event is sent through the %TransportStrategy itself (if supported)
     * or through a reliable middleware-internal channel (%IConnectionManager interface)
     *
     * @param event The event that should be sent
     * @return result of event
     */
    virtual Result sendReliableConfigEvent(Event& event) const;
    //@}

    /** @name DispatchedNetHandler methods
     */
    //@{
    /** Socket error notification.
     * This method is called if the event POLLERR occurs.
     * @param socket The corresponding socket.
     * @return VOID by default
     */
    virtual NetResult errorOccured(SocketHandle socket)       { return closeConnection(socket); }

    /** Invalid socket notification.
     * This method is called if the event POLLINVAL occurs.
     * @param socket The corresponding socket.
     * @return VOID by default
     */
    virtual NetResult invalidSocket(SocketHandle socket)      { return closeConnection(socket); }

    /** Connection shutdown notification.
     * This method is called if the event POLLHUP occurs.
     * @param socket The corresponding socket.
     * @return VOID by default
     */
    virtual NetResult closeConnection(SocketHandle socket);

    /** Incoming data notification.
     * This method is called if the event POLLIN, POLLRDNORM, POLLRDBAND, POLLPRI occurs, which means
     * that normal or priority Data can be read.
     * @param socket The corresponding socket.
     * @return VOID by default
     */
    virtual NetResult receiveData(SocketHandle socket);

    /** Check if more data is already available.
     *
     * This method is invoked by the leader thread before checking for I/O events. If it returns
     * @c false, then the leader thread checks for events on the socket. Otherwise, @c receiveEvent
     * is called directly with the same arguments
     */
    virtual bool isDataAvailable();

    /** Flushes the buffer receiver.
     *
     * This method flushes the buffer receiver.
     */
    virtual void flushITSReceiver() {};
    //@}

    /** @name Addresses and Connection Parameters
     */
    //@{
    /** Get the address of this transport strategy.
     * This method returns the address of this transport strategy.
     * it is used for indirect connection setup.
     *
     * @return Address of this transport strategy
     */
    virtual const UDPAddress& getLocalAddress() const throw() {
      return *__local_address;
    }

    /** Get the address of the partner strategy of this transport strategy.
     *
     * @return Address of the partner strategy of this transport strategy
     */
    const UDPAddress* getRemoteAddress() const {
      return __remote_address;
    }
    //@}
    
    /** @name Protocol Statistics
     */
    //@{
    /** Reset all statistics.
     * This method resets all statistics that can be queried by the other methods
     * to their initial values.
     */
    virtual void resetStatistics();

    /** Get total number of sent bytes.
     * This method returns the total number of bytes sent over the network.
     *
     * @return Total number of sent bytes
     */
    virtual nmm_uint32 getTotalSentBytes() const;

    /** Get total number of received packets.
     * This method returns the total number of packets which have been received from the network.
     *
     * @return Total number of received packets
     */
    virtual nmm_uint32 getTotalReceivedBytes() const;

    /** Get total number of sent packets.
     * This method returns the total number of packets which have been sent over the network.
     *
     * @return Total number of sent packets
     */
    virtual nmm_uint32 getTotalSentPackets() const;

    /** Get total number of received packets.
     * This method returns the total number of packets which have been received from the network.
     *
     * @return Total number of received packets
     */
    virtual nmm_uint32 getTotalReceivedPackets() const;

    /** Get total number of discarded packets.
     * This method returns the total number of packets which have been discarded because they arrived
     * out of order.
     *
     * @return Total number of discarded packets
     */
    virtual nmm_uint32 getTotalDiscardedPackets() const;

    /** Get total number of lost packets.
     * This method returns the total number of packets which are considered lost because they
     * were expected but did not arrive.
     *
     * @return Total number of lost packets
     */
    virtual unsigned int getTotalLostPackets() const;
    //@}
    
    /** @name Static type information.
     */
    //@{
    /** Get strategy type of this strategy class.
     *
     * @return "UDPStrategy"
     */
    static inline string getStaticStrategyType() { return __strategy_type; }
    //@}
    
  protected:
    /** @name Internal dispatch methods
     */
    //@{
    /** Dispatch an incoming message.
     * This method reads the message type of an incoming message from the socket
     * and invokes the correct dispatching method for the message type.
     *
     * @return ThreadedNetHandler result
     */
    virtual void dispatchMessage();

    /** Dispatch an event request.
     * This method reads and handles an out-of-band or instream reliable event request.
     *
     * @param iv Input value stream to use for serialization
     * @param message_type Type of message (EVENT_REQUEST or RELIABLE_EVENT_REQUEST)
     */
    void dispatchEventRequest(IValueStream& iv, UDPMessage message_type);

    /** Dispatch an event reply.
     * This method reads and handles an out-of-band or instream reliable event reply.
     *
     * @param iv Input value stream to use for serialization
     */
    void dispatchEventResponse(IValueStream& iv);

    /** Dispatch a one-way buffer.
     * This method reads and handles a buffer.
     *
     * @param iv Input value stream to use for serialization
     */
    void dispatchBufferOneWay(IValueStream& iv);

    /** Dispatch a one-way composite event.
     * This method reads and handles a composite event.
     *
     * @param iv Input value stream to use for serialization
     */
    void dispatchCEventOneWay(IValueStream& iv);
    //@}

    /** @name Methods to send Data
     */
    //@{
    /** Send an event request.
     * This method sends an event request and waits for a reply.
     *
     * @param e An event
     * @param message_type Type of event (EVENT_REQUEST, RELIABLE_EVENT_REQUEST, RELIABLE_CONFIG_EVENT_REQUEST)
     * @return Result of the event
     */
    Result sendRequest(const Value& e, UDPMessage message_type) const;

    /** Send an event reply.
     * This method sends an event reply. The reply consists of the event object and a result.
     *
     * @param result Event result
     * @param v The event
     * @param message_type Type of message (EVENT_RESPONSE)
     */
    void sendResponse(Result result, const Value* v, UDPMessage message_type);

    /** Send a one-way message.
     * This method queues the given message for sending.
     *
     * @param message A message
     * @param message_type Type of message (ONE_WAY_BUFFER or ONE_WAY_CEVENT)
     */
    Result sendOneWayMessage(const Message* message, UDPMessage message_type);

    /** Send queued message.
     * This method sends the currently queued message.
     *
     * @param message A message
     */
    void sendQueuedMessage(const Message* message);

    /** This method creates the UDP socket.
     *
     * @exception ConnectionException if the socket could not be created
     */
    virtual void createLocalSocket() throw(const ConnectionException&);

    /** This method connects the UDP socket to a peer address.
     *
     * @exception ConnectionException if the connection could not be established
     */
    virtual void connectToPeer() throw(const ConnectionException&);
    //@}

    /** @name Task Scheduling
     */
    //@{
    /** Main thread method.
     * This method handles incoming messages in a loop.
     * It runs in a separate thread owned by the transport strategy.
     */
    virtual void run();

    /** Wait for a task.
     * This method is called when the transport strategy thread is idle.
     */
    void conditionWait();

    /** Notify the transport strategy thread.
     * This method wakes up the transport strategy thread when it is idle.
     */
    void conditionNotify();
    //@}
    
    /** @name ValueStreams
     */
    //@{
    /** Sets the input value stream stack.
     *
     * Set the %ValueStream that should be used for serializing
     * outgoing data.
     *
     * This method should be used for setting the complete value
     * stream stack, including all configuration parameters of all
     * stream objects.
     *
     * @param value_stream The value stream object
     */
    virtual void setOValueStreamStack(const OValueStream* value_stream);

    /** Set the output value stream stack.
     *
     * Set the %ValueStream that should be used for deserializing
     * incoming data.
     *
     * This method should be used for setting the complete value
     * stream stack, including all configuration parameters of all
     * stream objects.
     *
     * @param value_stream The value stream object
     */
    virtual void setIValueStreamStack(const IValueStream* value_stream);

    /** Set the output value stream by type.
     *
     * Set the %ValueStream that should be used for serializing
     * outgoing data.
     *
     * This method only sets the top-level value stream, which is
     * created by the factory from the value stream type.
     *
     * @param value_stream_type Type name of the %ValueStream
     * @exception BindingException if a %ValueStream of the specified type isn't available
     */
    virtual void setOValueStream(const string& value_stream_type)
      throw(const BindingException&);

    /** Set the input value stream by type.
     *
     * Set the %ValueStream that should be used for deserializing
     * incoming data.
     *
     * This method only sets the top-level value stream, which is
     * created by the factory from the value stream type.
     *
     * @param value_stream_type Type name of the %ValueStream
     * @exception BindingException if a %ValueStream of the specified type isn't available
     */
    virtual void setIValueStream(const string& value_stream_type)
      throw(const BindingException&);
    //@}

    /** @name IByteCounter interface
     */
    //@{
    /** Get total number of bytes sent over the network.
     *
     * @return Total number of bytes sent over the network
     */
    virtual nmm_int32 getSendingCounter() const;

    /** Get total number of bytes received from the network.
     *
     * @return Total number of bytes received from the network
     */
    virtual nmm_int32 getReceivingCounter() const;

    /** Reset the counter for sent bytes.
     * This method resets the internal counter which is used by getSendingCounter()
     * to count the number of bytes sent over the network.
     */
    virtual void resetSendingCounter();

    /** Reset the counter for received bytes.
     * This method resets the internal counter which is used by getReceivingCounter()
     * to count the number of bytes received from the network.
     */
    virtual void resetReceivingCounter();
    //@}

  private:
    /** Close connection.
     * This is an internal method which is called when the connection is closed.
     *
     * @param socket Socket handle
     * @return ThreadedNetHandler result
     */
    NetResult internalCloseConnection(SocketHandle socket);

  private:
    SockDGram      __sock;      ///< The UDP socket

    /* Begin> Attributes for send/receiveEvent */
    ThreadMutex                   __event_mutex;                ///< Mutex for __event_condition
    ThreadCondition               __event_condition;            ///< Condition to be notified about event response
    ThreadMutex                   __event_sending_mutex;        ///< This mutex ensures that only one event can be send
    mutable Value*                __current_value;              ///< Pointer to the current Value that is send
    Result                        __current_result;             ///< Result for the returning Value
    /* End< */

    /* Begin> Pointers to the used ValueStreams */
    OValueStream*                 __o_stream;                   ///< OValueStream which is used for serialization
    IValueStream*                 __i_stream;                   ///< IValueStream which is used for deserialization
    /* End< */
    
    /* Begin> Pointers to the used NetStreams */
    ONetStream*                   __o_net_stream;               ///< Network output stream
    INetStream*                   __i_net_stream;               ///< Network input stream
    bool                          __use_packet_stream;          ///< Whether we are using a packet stream
    /* End< */
    
    /* Begin> Thread specific attributes */
    bool                          __is_running;                 ///< Threads running flag
    ThreadMutex                   __condition_thread_mutex;     ///< Mutex for Threads condition __thread_condition
    ThreadCondition               __thread_condition;           ///< Thread condition to wait if its nothing to do
    /* End< */

    /* Begin> Message sending attributes */
    Message*                      __send_message;               ///< Temporary pointer to the message that should be send
    ThreadMutex                   __send_message_mutex;         ///< Mutex for save message sending
    /* End< */

    /* Begin> Connection specific attributes */
    UDPAddress*                   __local_address;              ///< Local UDP address
    UDPAddress*                   __remote_address;             ///< Remote UDP address
    IConnectionManager*           __icm;                        ///< Connection manager
    NMMReference                  __remote_ts_ref;              ///< NMM reference of remote transport strategy
    /* End< */

    /* Begin> Attributes for incoming messages from network */
    bool                          __blocked;                    ///< Flag that is true if the parent returns blocked
    ThreadMutex                   __incoming_message_mutex;     ///< Mutex for the incoming messages
    ThreadMutex                   __socket_mutex;               ///< Mutex to protect the socket
    ThreadMutex                   __net_stream_mutex;           ///< Mutex to protect the net stream
    ThreadMutex                   __blocked_mutex;              ///< Mutex to protect the blocking state
    ThreadCondition               __blocked_condition;          ///< Condition variable to protect the blocking state
    /* End< */

    /* Begin> NetReactor event registration */
    int                           __udp_socket_events;          ///< Events the udp socket is currently registered for
    /* End< */

    /* Begin> UDP port range */
    static int                    __udp_min_port;               ///< Minimum UDP port
    static int                    __udp_max_port;               ///< Maximum UDP port
    /* End< */

  private:
    static const char __strategy_type[];                        ///< Static type name of this strategy
  };

}

#include "nmm/nmm_suffix.hpp"

#endif
