// This may look like C code, but it is really -*- C++ -*-


//<copyright>
//
// Copyright (c) 1993
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>



//<file>
//
// Name:       reader.C
//
// Purpose:    receive pipe-documents via socket (experimental)
//
// Created:    Sat Sep 25 11:06:40 METDST 1993 Peter Pichler
//
//
//
// Description:
// 
//      file has example-character for now
// 
// 	    - create a socket for receiving data on port or find a free port
//      - on a connect-request a connection will be established and 
//        data is delivered via inputReady(), so we dont need an extra
//        IOHandler -> derived classes would call their input-processing
//        routine from inside Reader::inputReady()
//      - new connect-requests during data-transfer can be refused, abandoned
//        or queued
//      - when a connection had been established, data can be peeked without
//        removing it from the stream
//
//      + in the future:
//        - pipe the input-stream through an external process (data-conversion)
//
//</file>




//<class>
//
// Name:       Reader
//
// Purpose:    open port, wait for connection(s), read data
//
//
//
// Public Interface:
//
//     Reader(port,options=0)
// 	    - create a socket for receiving data on port 
//          (port==0 -> find a free port)
//        - the Dispatcher will asynchronously listen for connections
//        - when a connection is established createReader() will delete our
//          port so there will be no connections allowed any more, and we will 
//          get our data via inputReady()
//        - or options together (REUSE|KILL)
// 
//     * option REUSE
//        - in addition keeps out port alive but unlinks it from the Dispatcher
//          so connections cat take place but no data will be read until the
//          primary connection terminates.
//          in that case the MOST RECENTLY requested connection will be 
//          established. 
//          (NOTE: LAST COME - FIRST SERVE !!!)
// 
//     * option KILL (NOT implemented yet)
//        - kill (accept&immediately close) any requested connection while
//          there is already one established
//
//     port() 
//        - return the port-number we're using
// 
// 
// Protected Interface:
//
//        - inputReady(fd) ... to be customized for own purpose
//                             (call own data-handling function)
//
//</class>



#include <sys/socket.h>


#include <Dispatch/dispatcher.h>
#include <Dispatch/rpcbuf.h>
#include <Dispatch/rpcservice.h>
#include <Dispatch/iohandler.h>
#if !defined (hpux) && !defined (UnixWare)
#include <osfcn.h>
#endif
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <InterViews/session.h>
#include <sys/resource.h>
#if !defined(hpux) && !defined(UnixWare)
#include <sysent.h>
#endif
#include <fcntl.h>

#ifdef HPUX
#include <unistd.h>
extern char* optarg;
extern int optind, opterr;
#endif

#define BUFSIZE 65536
char buffer[BUFSIZE];                               // input and output buffer


class Reader : public RpcService {               // RpcService is an IOHandler
public:
	Reader(int port, int options = 0);
	enum {REUSE=1,KILL=2};
	int port() {return _port;};
	int peek(char*,int);
protected:
	void createReader(int);
	int inputReady(int);
private:
	int connected;
	int portfd;
	int ourfd;                                                 // for peeking
	int options;
};

Reader::Reader(int port, int opt) : RpcService(port) {
//    cout << "Reader::Reader(" << port << ") -> " << _port <<endl;
	connected = 0;
	options   = opt;
	portfd    = -1000; //MAX_INT...
	ourfd     = -1000;
}

void Reader::createReader(int fd) {
	cout << "Reader createReader:" << fd << endl;

//(1) do this to REUSE service
	if(options&REUSE) {
		Dispatcher::instance().unlink(portfd); // dont listen for new connects
	}
//(2) this will delete out service ... do this to only load 1 Document
	else {
		cout << "Reader stopListening()" << endl;
		stopListening();
	}
//(END)

	cout << "Dispatcher link" << fd << endl;
	Dispatcher::instance().link( fd,Dispatcher::ReadMask,this );
	ourfd=fd;

	connected++;
	cout << "Reader connected=" << connected << endl;

}

int Reader::inputReady(int fd) {

	if(!connected) {
		cout << "conn=" <<connected 
			 << "calling RpcService::inputReady " << fd << endl;
		portfd=fd;
		return RpcService::inputReady(fd);
	}
	else
		if(fd==portfd) // another connect, while already reading...
		{
			// should not happen ?!?
			cout << "inputReady on portfd while connected !!!" << endl;
			exit(-1) ;
		}

	char buf[4096];
	int n;

// just for testing... normally the appclication may peek - typically during
//                     the startup to identify the data coming in ...
{
	n = peek(buf,4096);
	cout << "PEEK:" << n << "->" << buf << endl ;
}
	n = read(fd,buf,4096);
	if(n>0) {
		cout << "Reader::inputReady" << fd << "," << n << endl;
		if(!strncmp("QUIT",buf,4)) {
			cout << "QUIT !!!" << endl;
			close(fd);
			ourfd= -1000;
			if(options&REUSE) {
	 			connected=0; 
							// make Dispatcher listen for new connections
							// (or establishing an already waiting connection)
				Dispatcher::instance().link( portfd,Dispatcher::ReadMask,this );
			}
		Dispatcher::instance().unlink( fd );
	        ourfd= -1000;
			return -1; 								// problem ... !!!!!!!!
		}
	}
	else {
	cout << "!!! Reader::inputReady" << fd << "!!!" << n << endl;
//  !!! we could set connected to 0 to accept the next Document ...

//(1) REUSE
	if(options&REUSE) {
		cout << "!!! connected=0; unlink " << fd << " link " << portfd << endl;
		connected=0;
		Dispatcher::instance().unlink( fd );
		ourfd = -1000;
		Dispatcher::instance().link( portfd,Dispatcher::ReadMask,this );
		}
//(2) 
	else {
		cout << "!!! -> return -1;" << endl;
	}
//(END)

	return(-1);		// call me round-robin, we could as well call 
					// RpcService::inputReady(fd), so it will accept the call
					// (assuming it`s really a call, an no error...)

	}
	buf[n]=0;
	return 0;
}

int Reader::peek(char* buf,int len) {
	if(ourfd<0)
		return -1;

	return ::recv(ourfd,buf,len,MSG_PEEK);
}



int main(int argc, char** argv) {
	Session* session = new Session(argv[0],argc,argv);

	for(;;){
		Reader* my = new Reader(0);                   // 1 connection only
           cout << "Reader,once port=" << my->port() <<endl;

		Reader* my2 = new Reader(0,Reader::REUSE);    // reuse port
           cout << "Reader,REUSE port=" << my2->port() <<endl;

		session->run();
	       cout << "after session->run()" << endl;

		delete my;
		delete my2;
	}

}

