/*
** JNetLib
** Copyright (C) 2000-2007 Nullsoft, Inc.
** Author: Justin Frankel
** File: connection.h - JNL TCP connection interface
** License: see jnetlib.h
**
** Usage:
**   1. Create a WAC_Network_Connection object, optionally specifying a wa::Components::WAC_Network_AsyncDNS
**      object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto),
**      and the send and receive buffer sizes.
**   2. Call connect() to have it connect to a host/port (the hostname will be
**      resolved if possible).
**   3. call run() with the maximum send/recv amounts, and optionally parameters
**      so you can tell how much has been send/received. You want to do this a lot, while:
**   4. check get_state() to check the state of the connection. The states are:
**        WAC_Network_Connection::STATE_ERROR
**          - an error has occured on the connection. the connection has closed,
**            and you can no longer write to the socket (there still might be
**            data in the receive buffer - use recv_bytes_available()).
**        WAC_Network_Connection::STATE_NOCONNECTION
**          - no connection has been made yet. call connect() already! :)
**        WAC_Network_Connection::STATE_RESOLVING
**          - the connection is still waiting for a JNL_AsycnDNS to resolve the
**            host.
**        WAC_Network_Connection::STATE_CONNECTING
**          - the asynchronous call to connect() is still running.
**        WAC_Network_Connection::STATE_CONNECTED
**          - the connection has connected, all is well.
**        WAC_Network_Connection::STATE_CLOSING
**          - the connection is closing. This happens after a call to close,
**            without the quick parameter set. This means that the connection
**            will close once the data in the send buffer is sent (data could
**            still be being received when it would be closed). After it is
**            closed, the state will transition to:
**        WAC_Network_Connection::STATE_CLOSED
**          - the connection has closed, generally without error. There still
**            might be data in the receieve buffer, use recv_bytes_available().
**   5. Use send() and send_string() to send data. You can use
**      send_bytes_in_queue() to see how much has yet to go out, or
**      send_bytes_available() to see how much you can write. If you use send()
**      or send_string() and not enough room is available, both functions will
**      return error ( < 0)
**   6. Use recv() and recv_line() to get data. If you want to see how much data
**      there is, use recv_bytes_available() and recv_lines_available(). If you
**      call recv() and not enough data is available, recv() will return how much
**      data was actually read. See comments at the function defs.
**
**   7. To close, call close(1) for a quick close, or close() for a close that will
**      make the socket close after sending all the data sent.
**
**   8. delete ye' ol' object.
*/

#ifndef NULLSOFT_WAC_NETWORK_CONNECTION_H
#define NULLSOFT_WAC_NETWORK_CONNECTION_H

#include "netinc.h"

#include "wac_network_dns.h"
#include "wac_network_dns_api.h"

#include "wac_network_connection_api.h"

#include "../nu/RingBuffer.h"

#include <stddef.h>

#if defined(_MSC_VER) && (_MSC_VER < 1200)
typedef int intptr_t;
#endif

class WAC_Network_Connection : public api_connection, private Filler, private Drainer
{
public:
    typedef enum
    {
        STATE_ERROR        = CONNECTION_STATE_ERROR,
        STATE_NOCONNECTION = CONNECTION_STATE_NOCONNECTION,
        STATE_RESOLVING    = CONNECTION_STATE_RESOLVING,
        STATE_CONNECTING   = CONNECTION_STATE_CONNECTING,
        STATE_CONNECTED    = CONNECTION_STATE_CONNECTED,
        STATE_CLOSING      = CONNECTION_STATE_CLOSING,
        STATE_CLOSED       = CONNECTION_STATE_CLOSED,
        STATE_RESOLVED     = CONNECTION_STATE_RESOLVED,
    } state;

    /*
    **  Joshua Teitelbaum, 1/27/2006 adding virtual
    */
    WAC_Network_Connection();
    WAC_Network_Connection( api_dns *dns, size_t sendbufsize, size_t recvbufsize );
    virtual ~WAC_Network_Connection();

    void open( api_dns *dns = API_DNS_AUTODNS, size_t sendbufsize = 8192, size_t recvbufsize = 8192 );
    void connect( const char *hostname, int port );
    virtual void connect( SOCKET sock, sockaddr *addr, socklen_t length /* of addr */ ); // used by the listen object, usually not needed by users.
    int set_recv_buffer_size( size_t new_buffer_size );
    /*
    **  Joshua Teitelbaum 2/2/2006
    **  Need to make this virtual to ensure SSL can init properly
    */
    virtual void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL );
    int  get_state()
    {
        return m_state;
    }
    char *get_errstr()
    {
        return m_errorstr;
    }

    void close( int quick = 0 );
    void flush_send( void )
    {
        send_buffer.clear();
    }

    size_t send_bytes_in_queue( void );
    size_t send_bytes_available( void );
    int send( const void *data, size_t length ); // returns -1 if not enough room
    inline int send_bytes( const void *data, size_t length )
    {
        return send( data, length );
    }
    int send_string( const char *line );         // returns -1 if not enough room

    size_t recv_bytes_available( void );
    size_t recv_bytes( void *data, size_t maxlength ); // returns actual bytes read
    unsigned int recv_int( void );
    int recv_lines_available( void );
    int recv_line( char *line, size_t maxlength ); // returns 0 if the line was terminated with a \r or \n, 1 if not.
    // (i.e. if you specify maxlength=10, and the line is 12 bytes long
    // it will return 1. or if there is no \r or \n and that's all the data
    // the connection has.)
    size_t peek_bytes( void *data, size_t maxlength ); // returns bytes peeked

    unsigned long get_interface( void );          // this returns the interface the connection is on
    unsigned long get_remote( void );             // remote host ip.
    unsigned short get_remote_port( void );       // this returns the remote port of connection
    void set_dns( api_dns *dns );
    void reuse();

protected:
    SOCKET m_socket;
    unsigned short m_remote_port;

    RingBuffer recv_buffer;
    RingBuffer send_buffer;

    addrinfo *saddr;
    sockaddr *address;

    char     m_host[ 256 ];

    api_dns *m_dns;
    bool     m_dns_owned;

    state m_state;
    char  *m_errorstr;

    /*
     **  Joshua Teitelbaum 1/27/2006 Adding new BSD socket analogues for SSL compatibility
     */
    virtual void    socket_shutdown();
    virtual ssize_t socket_recv( char *buf, size_t len, int options );
    virtual ssize_t socket_send( const char *buf, size_t len, int options );
    virtual int     socket_connect();
    virtual void    on_socket_connected();

private:
    void init(); // constructor helper function

    // functions for RingBuffer
    size_t Read( void *dest, size_t len ) override;
    size_t Write( const void *dest, size_t len ) override;

protected:
    RECVS_DISPATCH;
};

#endif  //!NULLSOFT_WAC_NETWORK_CONNECTION_H