/** \file
 *
 *  Contains the Socket class declaration.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef SOCKET_H_
#define SOCKET_H_

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>

#include "SocketException.h"

const int MAXHOSTNAME = 200;
const int MAXCONNECTIONS = 5;
const int MAXRECV = 500;
const int SIMPORT = 54327;

/**
 *  A wrapper around sys/socket.h  for tcp communications between
 *  Socket instances -- use daughter classes SocketClient or
 *  SocketServer to do any real work.
 *
 *  \ingroup modules_simulator
 */
class Socket
{
public:

    virtual ~Socket();
    // Data Transimission
    int send( const char* buffer, size_t bufferLength ) const
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;
    /// Like send but making sure the whole requested buffer is sent out.
    /// Returns number of bytes sent, or:
    /// -2:  unconnected socket
    /// -1:  some system error; check errno
    /// Note: for the moment using return code for error condition instead of throwing exception.
    int sendall( const char* buffer, size_t bufferLength );

    /// Blocking read
    int recv( char* buffer, size_t bufferLength ) const
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    /// Socket read with timeout.
    /// NOTE: assumes the socket is in blocking mode: it sets it to non-blocking for the
    /// internal operation, and sets it back to blocking before returning.
    int recv( char* buffer, size_t bufferLength, int timeoutMillis )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    int setTimeout( const int milliseconds );
    int close();
    int getPort()
    {
        return addr_.sin_port;
    }

    /// exposes the socket descriptor to facilitate direct operations
    int getDescriptor()
    {
        return sock_;
    }

protected:

    Socket();

    // Server initialization
    void create()
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;
    void bind( const int port )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;
    void listen() const
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;
    void accept( Socket& newSocket )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    /// accept with given timeout, which could be 0 for completely non-blocking behavior
    void accept( Socket& newSocket, int timeoutMillis )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    // Client initialization
    void connect( const char* host, const int port )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    /// connect with timeout.
    /// See SocketClient( const char* host, int port, int timeoutMillis ) constructor
    /// in SocketClient.h for general documentation about this connection mode.
    void connect( const char* host, const int port, int timeoutMillis )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    void setNonBlocking( const bool nonBlocking )
#ifndef __GAZEBO
    throw( SocketException& )
#endif
    ;

    bool isValid() const
    {
        return sock_ != -1;
    }

    void setTimeval( const int milliseconds, struct timeval& tv );

private:

    int sock_;
    sockaddr_in addr_;
    bool connected_;
};

#endif /*SOCKET_H_*/
