/*
 * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University
 * Copyright (c) 1991 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Stanford and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Stanford and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

#ifndef dp_rpcbuf_h
#define dp_rpcbuf_h

#include "libdispatch.h"

#include "iostreamb.h"

#include <hyperg/utils/verbose.h>

// Specialize streambuf to sending and receiving RPC requests to and
// from remote machines.

// jf: policy of handling requests

// WRITING
// 
// rpcstream& operator<<(rpcstream&, const RpcHdr&) (in rpchdr.[hC])
// mainly calls start_request() to set a pointer to the beginning of the
// request inside the put area and to reserve space for the length field
// there. That field is inserted later at finish_request(). That operator
// also inserts itself into this.
// 
// Any data which make up the "arguments" of the request may be inserted
// as if they were regular stream data.
// 
// A request is terminated by either inserting another request, or by
// calling ostream::flush(). Both in turn call finish_request() in the
// following ways:
// 
// - Inserting another request (via the above operator) calls
//   finish_request() (even if there is no request yet inserted).
// 
// - Calling ostream::flush() calls the virtual streambuf method
//   overflow() with the EOF character. rpcbuf::overflow (EOF) will then
//   call finish_request() in turn.
// 
// finish_request() will insert the length into the space of the put
// area reserved as described above, and set the request pointer to
// nil, thus "converting" the request's data to regular stream
// data. Regular stream data will be output in the write loop in
// rpcbuf::overflow().
// 
// If the request pointer (rptr()) is set, overflow() will write any
// data preceding the request (i.e., the "converted" data of the
// previous request) in the put area out to the socket. If there is no
// data there, it will do nothing but append the overflow character
// (either to the currnt request's "arguments", or, if none, to the
// stream's data). Note that, if the overflow character is EOF,
// overflow() will itself "convert" a not-yet-terminated request to
// normal data. So, when it gets to writing afterwards, there *will*
// be data to write out (provided that a request was there).

class rpcbuf : public streambuf {
public:
   static Verbose verbose ;

public:
    rpcbuf(iostreamb* = nil);
    virtual ~rpcbuf();

    const char* host();
    int port();
    int fd();
    boolean opened();
    boolean nonblocking();

    enum { anyport = 0 };
    rpcbuf* listen(int port);
    rpcbuf* connect(const char* host, int port);
    rpcbuf* accept(int& fd);
    rpcbuf* attach(int fd);
    rpcbuf* nonblocking(boolean);
    rpcbuf* setVerbose(boolean);
    rpcbuf* close();
    int start_request();
    int read_request();

    virtual int overflow(int c = EOF);
    virtual int underflow();
#ifdef WIN32
    int readon();
#endif
    virtual int sync();
#if defined(cplusplus_2_1) || defined(WIN32)
    virtual streampos seekoff(streamoff, ios::seek_dir, int);
#else
    virtual streampos seekoff(streamoff, seek_dir, int);
#endif
    virtual streambuf* setbuf(char*, int);
protected:
    virtual int doallocate();
    void finish_request();
    boolean expand_g(int);
    boolean expand_p();
    void error(const char*);
    void sys_error(const char*);
    iostreamb& mystream();
    char* rptr();
    void setr(char*);
    void rbump(int);
protected:
    iostreamb* _mystream;	// reference to the stream that uses me
    char* _rptr;		// beginning of outgoing RPC request
    int _actualWidth;		// width of outgoing RPC request's length field
    const char* _host;		// name of my peer's host or nil if no peer
    int _port;			// my peer's port or my port if I'm a listener
    int _fd;			// my IPC connection's file descriptor
    boolean _opened;		// do I have an open file descriptor?
    boolean _close;		// should I close my file descriptor on exit?
    boolean _nonblocking;	// can I read or write without blocking?
    boolean _verbose;		// should I print system error messages?

private:
   // jfasch 24 Apr 1995:
   // wrapped around read'n'write to prevent from being damaged by a signal
   int rpc_read (int fd, char* buf, int len);
   int rpc_write (int fd, const char* buf, int len);

public:
   // link time assertion
   static const char* version1 ;
};
static const char* hg_common_dispatch_rpcbuf_version = rpcbuf :: version1 ;

// Get the stream which will format the length field of RPC requests.

inline iostreamb& rpcbuf::mystream() {
    return *_mystream;
}

// Get and set the pointer to the outgoing RPC request's beginning.

inline char* rpcbuf::rptr() {
    return _rptr;
}

inline void rpcbuf::setr(char* rptr) {
    _rptr = rptr;
}

inline void rpcbuf::rbump(int n) {
    _rptr += n;
}

#endif
