/* 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_SOCKCONNECTOR_HPP
#define NMM_SOCKCONNECTOR_HPP NMM_SOCKCONNECTOR_HPP

#include "nmm/nmm_prefix.hpp"

#include "Sock.hpp"

#include "nmm/utils/time/Interval.hpp"

#include "visibility.hpp"

namespace NMM {

  /** This wrapper class can be used to establish a new connection and is a wrapper class
   * for the connect and disconnect method.
   * With tcp, this is a wrapper for the socket(2) and connect(2) functions.
   * With udp, a dummy implementation is provided which simply creates a udp socket and sets its
   * peer address to the server address. No emulation of tcp functionality is done and no requests
   * are actually sent to the server udp socket.
   */
  class DLLEXPORT SockConnector {
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Default Constructor.
     */
    SockConnector();

    /** Destructor.
     */
    ~SockConnector();
    //@}

    /** @name Connection Methods
     */
    //@{
    /** Establish a connection to the given address and create a new socket object.
     *
     * If the transport protocol is "udp", the connection always trivially succeeds,
     * and a pointer to a new SockDGram object is returned.
     *
     * If the transport protocol is "tcp", then this method establishes a TCP connection. The operation
     * blocks until the connection has either succeeded, failed or timed out. If the conection has
     * failed or timed out, an exception is thrown. The connection timeout can be set by calling
     * setConnectionTimeout(). If the connection succeeds, a pointer to a new SockStream object is returned.
     *
     * Other transport protocols than "udp" and "tcp" are not supported.
     *
     * This method creates a new SockStream or SockDGram object (depending on the transport protocol)
     * which must be deleted by the caller.
     *
     * @param addr The address to connect to
     * @param proto The transport layer protocol that is used for the connection
     * @return A pointer to a new Sock or SockStream that can be used to transport or receive data
     * @exception ConnectionException if the connection fails or times out or the transport protocol is not "tcp" or "udp"
     */
    Sock* connect(const INetAddr& addr, const char* proto = "tcp")
      throw (const ConnectionException&);

    /** Establish a connection to the given address using an existing socket object.
     *
     * This method sets the peer address of the Sock object if the connection request was successful.
     * If the transport protocol is "udp", the connection always trivially succeeds.
     * The given Sock object must be a SockDGram.
     *
     * If the transport protocol is "tcp", then this method establishes a TCP connection. The operation
     * blocks until the connection has either succeeded, failed or timed out. If the conection has
     * failed or timed out, an exception is thrown. The connection timeout can be set by calling
     * setConnectionTimeout().
     * The given Sock object must be a SockStream.
     *
     * Other transport protocols than "udp" and "tcp" are not supported.
     *
     * This method sets the peer address of the given Sock object if the connection request was successful.
     *
     * @param addr The address to connect to
     * @param proto The transport layer protocol that is used for the connection
     * @return A pointer to a new Sock or SockStream that can be used to transport or receive data
     * @exception ConnectionException if the connection fails or times out or the transport protocol is not "tcp" or "udp"
     */
    void connect(Sock& sock, const INetAddr& addr, const char* proto = "tcp")
      throw (const ConnectionException&);
    //@}

    /** @name Configuration
     */
    //@{
    /** Set the connection timeout.
     *
     * @param connect_timeout The connection timeout
     */
    void setConnectionTimeout(const Interval& connect_timeout) { __connect_timeout = connect_timeout; }

    /** Get the connection timeout.
     *
     * @return The connection timeout
     */
    const Interval& getConnectionTimeout() const { return __connect_timeout; }
    //@}

  private:
    /** Disabled copy constructor.
     */
    SockConnector(const SockConnector&) throw() {}

    /** Disabled assignment operator.
     */
    SockConnector& operator=(const SockConnector&) throw() { return *this; }

    /** @name Exception methods
     * These methods create exceptions from an errno value
     */
    //@{
    /** Creates an Exception that occurs during establishing of a new connection. Based on the given error code a error message is 
     * created and stored in Exception. 
     *
     * @param _errno specifies the error code
     */
    void createConnectionException(int _errno);

    /** Creates an Exception that occurs during instantiation of a new socket. Based on the given error code a error message is 
     * created and stored in Exception. 
     *
     * @param _errno specifies the error code
     */
    void createSocketException(int _errno);

    /** Creates an Exception that occurs during shutting down of a connection. Based on the given error code a error message is 
     * created and stored in Exception. 
     *
     * @param _errno specifies the error code
     */
    void createShutdownException(int _errno);
    //@}

    /** Internal connection method.
     *
     * @param socket Existing socket handle (can be -1 for "tcp", must not be -1 for "udp")
     * @param addr The address to connect to
     * @param proto The transport layer protocol that is used for the connection
     * @return Either the value of @c socket or a new socket handle
     * @exception ConnectionException if the connection fails or times out or the transport protocol is not "tcp" or "udp"
     */
    SocketHandle internalConnect(SocketHandle socket, const INetAddr& addr, const char* proto) throw(const ConnectionException&);

  private:
    Interval __connect_timeout;
  };
  //@}

}

#include "nmm/nmm_suffix.hpp"

#endif
