#ifndef TCPSOCK_H
#define TCPSOCK_H 1

#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

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

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() );
    }
};
#endif /*TCPSOCK_H*/