/* 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_TCP_STRATEGY_HPP
#define NMM_TCP_STRATEGY_HPP NMM_TCP_STRATEGY_HPP

#include "nmm/nmm_prefix.hpp"

#include "IPGeneric.hpp"
#include "ITCPControlImpl.hpp"
#include "IByteCounterImpl.hpp"

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

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

#include "nmm/utils/net/SockConnector.hpp"
#include "nmm/utils/net/SockStream.hpp"
#include "nmm/utils/net/ThreadedNetHandler.hpp"

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

#include "visibility.hpp"

#include "nmm/nmm_hash_map.hpp"

namespace NMM {

  class IValueStream;
  class OValueStream;
  class INetStream;
  class ONetStream;

  class TCPAddress;

  class Message;

  /** TCP transport strategy.
   * This class implements a transport strategy which uses the Transport Control Protocol (TCP/IP).
   * It is the default transport strategy used for network communication in NMM.
   *
   * 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 reliable and supports 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 BufferedINetStream and BufferedONetStream are used as network streams.
   * It is possible to set different stream classes using the ISetValueStream interface
   *
   * The IPControl and ITCPControl interfaces can be used to set protocol-specific options.
   *
   * This transport strategy uses the SockStream class to handle TCP sockets.
   * Event notification is performed using the NetReactor and a ThreadedNetHandler.
   *
   * @see TCPAddress, TCPAcceptor, SockStream, BufferedINetStream, BufferedONetStream, MagicNumberIValueStream, MagicNumberOValueStream
   */
  class DLLEXPORT TCPStrategy
    : public TransportStrategy,
      public IPGeneric,
      public ITCPControlImpl,
      public ITSRequestReplyEventReceiver,
      public ITSOneWayCEventReceiver,
      public ITSOneWayBufferReceiver,
      public ITSRequestReplyEventSender,
      public ITSOneWayCEventSender,
      public ITSOneWayBufferSender,
      public ThreadedNetHandler,
      public IByteCounterImpl,
      public ISetValueStreamImpl,
      protected ThreadedObject
  {
  private:
    /** TCP message type. */
    enum TCPMessage {
      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)
      RELIABLE_EVENT_REQUEST          = 4, ///< Reliable internal event request (Event)
      RELIABLE_CONFIG_EVENT_REQUEST   = 5  ///< Reliable internal configuration event request (Event)
    };

    /** ID of an out-of-band request, used for associating requests with replies. */
    typedef unsigned int RequestID;

  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Constructor.
     *
     * @param name name of this object
     */
    TCPStrategy(const string& name = "TCPStrategy") ;

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

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

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

    /** @name IPGeneric Implementation
     */
    //@{
    /** 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 getStreamSocket();
    }

    /** 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 getStreamSocket();
    }

    /** Get the stream 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       SockStream& getStreamSocket()       { return __stream; }

    /** Get the stream 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 SockStream& getStreamSocket() const { return __stream; }
    //@}

    /** @name Receiver Interfaces
     */
    //@{
    /** 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);

    /** Flushes the TCPStrategy
     */
    virtual void flushITSReceiver();
    //@}

    /** @name Configuration
     */
    //@{
    /** Sets address and connect to peer.
     *
     * This method connects this %TransportStrategy with its peer.
     * The given peer address must be of type TCPAddress.
     *
     * @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 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);

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

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

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

    /** This method checks if the strategy is connected.
     *
     * @return true if the strategy is connected, false otherwise
     */
    virtual bool isConnected()  const {
      return __is_running;
    }
    //@}
    
    /** @name ThreadedNetHandler 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();
    //@}

    /** @name ITCPControl specific methods
     */
    //@{
    /** Enable or disable no-delay option for TCP .
     * This method enables or disables the no-delay option for TCP.
     *
     * If the no-delay option is enabled, Nagle's algorithm is turned off.
     * This means that packets are always sent as soon as possible,
     * and no additional delays are introduced.
     * This comes at the cost of more packets sent over the network.
     * The no-delay option is disabled by default.
     *
     * This option corresponds to the TCP_NODELAY option under Linux.
     *
     * @param value @c true to enable the no-delay option, @c false to disable the no-delay option
     */
    virtual void enableNoDelay(const nmm_bool& value);

    /** Check if the no-delay option for TCP is enabled.
     * This method returns a flag which indicates whether the no-delay option for TCP is enabled.
     *
     * If the no-delay option is enabled, Nagle's algorithm is turned off.
     * This means that packets are always sent as soon as possible,
     * and no additional delays are introduced.
     * This comes at the cost of more packets sent over the network.
     * The no-delay option is disabled by default.
     *
     * This option corresponds to the TCP_NODELAY option under Linux.
     *
     * @note This method should be called isNoDelayEnabled(). Don't be confused by its name!
     *
     * @return @c true if the no-delay option is enabled, @c false otherwise
     */
    virtual nmm_bool isDelayEnabled() const;

    /** Set maximum TCP segment size.
     * This method sets the maximum segment size (MSS) used by TCP.
     *
     * This option corresponds to the TCP_MAXSEG option under Linux.
     *
     * @param mss Maximum segment size (in bytes)
     */
    virtual void setMaxSegment(const nmm_int32& mss);

    /** Get maximum TCP segment size.
     * This method gets the maximum segment size (MSS) used by TCP.
     *
     * This option corresponds to the TCP_MAXSEG option under Linux.
     *
     * @return Maximum segment size (in bytes)
     */
    virtual nmm_int32 getMaxSegment() const;

    /** Disable or enable sending of partial TCP frames.
     * This method disables or enables sending of partial TCP frames. If sending of partial frames.
     *
     * @note This method should be called disablePartialFrames. Don't be confused by its name!
     *
     * @param enable @c true to disable sending of partial frames, @c false to enable sending of partial frames
     */
    virtual void enablePartialFrames(const nmm_bool& enable);

    /** Check if sending of partial frames is disabled.
     * This method returns a flag which indicates whether sending of partial frames is disabled.
     *
     * @note This method should be called isPartialFramesDisabled. Don't be confused by its name!
     *
     * @return @c true if sending of partial frames is disabled, @c false otherwise
     */
    virtual nmm_bool isPartialFramesEnabled();

    /** Get unread queue count.
     *
     * @return Unread queue count
     *
     * @deprecated This method is obsolete and has undefined behaviour
     */
    virtual nmm_int32 getUnreadQueueCount() const;

    /** Get unsend queue count.
     *
     * @return Unsend queue count
     *
     * @deprecated This method is so obsolete, we don't even know if the spelling mistake in its name was intentional
     */
    virtual nmm_int32 getUnsendQueueCount() const;

    /** Check if all urgent data send.
     *
     * @return @c true if all urgent data send, @c false otherwise
     *
     * @deprecated This method is so obsolete, we don't even know if the spelling mistake in its name was intentional
     */
    virtual nmm_bool allUrgentDataSend()   const;

    /** Get unsend queue count.
     *
     * @return Unsend queue count
     *
     * @deprecated This method is obsolete and has undefined behaviour
     */
    virtual void enableKeepAlive(const nmm_bool& value);

    /** This method returns true if a connection is keeped alive, otherwise false.
     */
    virtual nmm_bool getKeepAlive() const;
    //@}

    /** @name Static type information.
     */
    //@{
    /** Get strategy type of this strategy class.
     *
     * @return "TCPStrategy"
     */
    static inline string getStaticStrategyType() { return __strategy_type; }
    //@}

  protected:
    /** @name Internal Dispatching 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 NetHandler::NetResult 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, TCPMessage 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);

    /** Dispatch a configuration event request.
     * This method reads and handles an instream reliable configuration event request.
     *
     * @param iv Input value stream to use for serialization
     */
    void dispatchConfigEventRequest(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 Event& e, TCPMessage 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 e The event
     * @param request_id Request ID of the event
     * @param message_type Type of message (EVENT_RESPONSE)
     */
    void sendResponse(Result result, const Event* e, RequestID request_id, TCPMessage 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, TCPMessage message_type);

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

    /** Establish connection.
     * This method establishes a connection to the currently set peer address.
     */
    virtual void connectTo()
      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 ISetValueStream interface
     */
    //@{
    /** 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:
    SockStream      __stream;   ///< The TCP socket

    /* Begin> Request/Reply state */
    /** State of a pending event request.
     */
    struct RequestState {
      Event* event;         ///< The event
      Result result;        ///< Result of the event (if a reply has been received)
      bool received_reply;  ///< A reply has been received
    };

    /** A request map.
     * This map stores state information for pending event requests,
     * indexed by request IDs.
     */
    typedef HASH_MAP_STL::hash_map<RequestID, RequestState> RequestMap;

    ThreadMutex                   __event_mutex;                ///< Mutex for __event_condition
    ThreadCondition               __event_condition;            ///< Condition to be notified about event response
    ThreadMutex                   __event_sending_mutex;        ///< Mutex for protecting event sending components
    mutable RequestID             __request_count;              ///< Request counter for generating request IDs
    mutable RequestMap            __request_map;                ///< Request map
    /* 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
    /* 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 */
    const Message*                __send_message;               ///< Temporary pointer to the message that should be send
    const Message*                __blocked_message;            ///< Hack to avoid problems if BLOCKED is ignored
    ThreadMutex                   __send_message_mutex;         ///< Mutex for save message sending
    /* End< */

    /* Begin> Connection specific attributes */
    SockConnector                 __connector;                  ///< Used SockConnector to establish a connection
    TCPAddress*                   __current_address;            ///< Pointer to the Current Address
    /* 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                   __blocked_mutex;              ///< Mutex to protect the blocking state
    ThreadCondition               __blocked_condition;          ///< Condition variable to protect the blocking state
    /* End< */

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

}

#include "nmm/nmm_suffix.hpp"

#endif
