#ifndef TCPSOCK_H
#define TCPSOCK_H 1

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef __cplusplus

#include <istream>
#include <ostream>
#include <streambuf>
#include <iosfwd>
#include <iostream>
#include <ext/stdio_filebuf.h>	// GNU C++ extension -- for making a filebuf from a unix file descriptor

class fdistream : public std::istream {
    int fd_;
    FILE *f_;
    typedef __gnu_cxx::stdio_filebuf<char>  stdio_filebuf;
    stdio_filebuf fdsb_;

  public:

    fdistream( int fd );
    ~fdistream();
};

class TCPsocket {
    int fd_;
    enum sockstate { INVALID, LISTENING, CONNECTING, CONNECTED } state_;
    int backlog_;
    bool nonblock_;
    struct sockaddr_in sin_;

    bool mksock( const char *name, bool nonblock );
    bool do_accept();
    void connectwith( const char *name, int port, bool nonblock );
    void listenwith( int listenport, bool await_first_contact, int backlog, bool nonblock );


  public:
    TCPsocket( const char *name, int port, bool nonblock = false );	// connect()
    TCPsocket( int listenport, bool await_first_contact = true, int backlog = 5, bool nonblock = false ); // listen()
    TCPsocket( TCPsocket &listener, bool nonblock = false );
    ~TCPsocket();

    bool valid() const { return state_ != INVALID; }
    enum sockstate state() const { return state_; }
    bool connected() const { return state_ == CONNECTED; }
    bool listening() const { return state_ == LISTENING; }

    void nonblock( bool nb );

    bool be_connected();				// wait for connection; false on failure

    int fd() const { return fd_; }			// suitable for select()ing

    bool ready();
    bool await( int usecs = -1 );

    fdistream *new_istream() {
	if(!be_connected())
	    return 0;
	return new fdistream( fd() );
    }
};

#define EXTERNC  extern "C"

#else /* not C++, just plain C */

struct TCPsocket;	/* opaque structure */

#define EXTERNC  extern

#endif

/* C interface */
EXTERNC struct TCPsocket *tcpconnect( char *name, int port, int nonblock );
EXTERNC struct TCPsocket *tcplisten( int port, int await_first_contact, int backlog, int nonblock );
EXTERNC struct TCPsocket *tcpaccept( struct TCPsocket *listener, int nonblock );

EXTERNC void  tcpclose( struct TCPsocket *ts );
EXTERNC int   tcpvalid( struct TCPsocket *ts );
EXTERNC int   tcpconnected( struct TCPsocket *ts );
EXTERNC int   tcplistening( struct TCPsocket *ts );
EXTERNC void  tcpnonblock( struct TCPsocket *ts, int nonblock );
EXTERNC int   tcpbe_connected( struct TCPsocket *ts );
EXTERNC int   tcpfd( struct TCPsocket *ts );
EXTERNC int   tcpawait( struct TCPsocket *ts, int maxusecs );	/* any data ready? wait up to that long, -1 => forever */

#undef EXTERNC


#endif /*TCPSOCK_H*/