// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1997
//  wer auch immer ...
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        tiorpcreader.C
// 
// Purpose:     
// 
// Created:     20 Feb 1997   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: a copy of rpcreader.C
// 
// $Id: tiorpcreader.C,v 1.3 1997/02/21 16:38:57 jfasch Exp $
// 
// $Log: tiorpcreader.C,v $
// Revision 1.3  1997/02/21 16:38:57  jfasch
// WIN32 oasch
//
// Revision 1.2  1997/02/21 16:23:29  jfasch
// return from inputReady()
//
// Revision 1.1  1997/02/20 19:32:25  jfasch
// Initial revision
//
// 
// </file> 
#include "tiorpcreader.h"

#include "tiorpchdr.h"
#include "tiorpcstream.h"

#include <hyperg/utils/assert.h>


// Prepare to read RPC requests from somebody else's connection or
// prepare to handle RPC requests read by somebody else.  Assume the
// I/O format has already been negotiated.  Start listening for RPC
// requests on the connection, taking into account the special case
// where RPC requests have already been buffered.  Zero the function
// array that a derived class will initialize with addresses of static
// member functions to unmarshall RPC requests.

TIORpcReader::TIORpcReader (TIOrpcstream* clint, int nfcns)
: _nfcns(nfcns),
  _function(new PF[nfcns]),
  _client(clint) {
     if (clint) 
        tio_ = clint->rdbuf()->tio() ;

     if (_client) {
        tio_.ptr()->registerInput (this) ;
	if (client().rdbuf()->read_request() != EOF) {
           hgassert (false, "TIORpcReader::TIORpcReader(TIOrpcstream*,int): "
                     "stream already contains a request "
                     "(passing \'em around is not implemented)") ;
	}
     }

     for (int i = 0; i < nfcns; i++) {
	_function[i] = nil;
     }
}

// Connect to a client through an already open file number.  Negotiate
// the I/O format.  Start listening for RPC requests on the
// connection, taking into account the special case where RPC requests
// have already been buffered (when the I/O format was negotiated).
// Zero the function array that a derived class will initialize with
// addresses of static member functions to unmarshall RPC requests.

TIORpcReader::TIORpcReader (const TransparentIOPtr& tio, int nfcns, boolean binary)
: _nfcns(nfcns),
  _function(new PF[nfcns]),
  _client(new TIOrpcstream),
  tio_(tio) {
     client().attach (tio);
     client().negotiate(binary);
     tio_.ptr()->registerInput (this) ;
     if (client().rdbuf()->read_request() != EOF) {
        hgassert (false, "TIORpcReader::TIORpcReader(TIOrpcstream*,int): "
                  "stream already contains a request (how can he? I just made him.)") ;
     }

     for (int i = 0; i < nfcns; i++) {
        _function[i] = nil;
     }
}

// Stop listening for RPC requests on the connection if we have one.
// Delete the connection if we created it ourselves.  Note that
// deleting a connection attached to a file number will not close that
// file number.

TIORpcReader::~TIORpcReader() {
   if (tio_) 
      tio_.ptr()->registerInput (nil) ;
    delete[] _function;
}

// Read only one RPC request per call to allow the program to
// interleave RPC requests from multiple clients.  Look up the proper
// reader to execute the request or skip over the request's data if it
// could not be executed.  Ask a derived class to take the appropriate
// action (perhaps closing the file number or deleting ``this'') if no
// more data is available or the data wasn't what we expected.

void TIORpcReader::inputReady (const TransparentIO* tio) {
   hgassert (tio==tio_.ptr(), "TIORpcReader::inputReady(): not my io") ;

   TIORpcHdr hdr;
   client() >> hdr;

   if (client().good() && !client().incomplete_request()) {
      TIORpcReader* reader = map(hdr.reader());

      if (!execute(reader, hdr)) {
         client().ignore(hdr.ndata());
      }
   }

   if (client().eof() || client().fail()) {
      tio_.ptr()->registerInput (nil) ;
      tio_.ptr()->registerOutput (nil) ;
      connectionClosed (tio_.ptr());
   } else if (client().incomplete_request()) {
      return ;		// call me only when more input arrives
   } else {
      return ;	// ?	// call me again as soon as possible
   }
}

// Map the number to the reader that should unmarshall the RPC
// request.  Return this reader itself by default; derived classes
// could return a different reader.

TIORpcReader* TIORpcReader::map(unsigned long) {
   return this;
}

// Look up the static member function that will unmarshall the RPC
// request's arguments and execute the request.  Call that function
// with the reader, RPC request header, and connection as arguments.
// Return true if the function was called, false otherwise.

boolean TIORpcReader::execute (TIORpcReader* reader, TIORpcHdr& hdr) {
   if (!reader) {
      return false;
   }

   if (hdr.request() < 0 || hdr.request() >= reader->_nfcns) {
      return false;
   }

   PF func = reader->_function[hdr.request()];
   if (!func) {
      return false;
   }

   (*func)(reader, hdr, client());
   return true;
}
