// 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:        dcsender.h
// 
// Purpose:     send documents to the document server
// 
// Created:     6 Dec 93   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// 
// </file> 
#ifndef hg_hyperg_dcsender_h
#define hg_hyperg_dcsender_h

#include <hyperg/hyperg/hgheader.h>

#include <Dispatch/rpcstream.h>
#include <Dispatch/iohandler.h>

class fstream ;
class ifstream ;



// <class> 
//  
// Name:             DcSender
// 
// Purpose:          base class of classes that send documents to the document server
// 
// Public Interface: 
// 
// - DcSender (int port, int maxread, long timeout)
// - DcSender (const HgHeader&, int port, int maxread, long timeout)
//   constructors;
//   mainly do nothing but setting up the internal things.
//   the whole thing is started with the protected method init_().
// 
// - virtual ~DcSender()
//   destructor
// 
// - int port() const 
//   returns the number of the port the object created
// 
// - boolean ok() const 
//   operator void*() const
//   boolean operator !() const
//   check whether an error occured 
// 
// - DcSState state() const 
//   returns the error state of this
// 
// - virtual int inputReady (int)
//   virtual int outputReady (int)
//   these are called by the Dispatcher and should not be used elsewhere
// 
// Protected Interface:
// 
// - void set_state_(DcSState)
//   derived classes, which will set up the source to read from, may encounter 
//   an error in doing so. call this to tell their base.
// 
// - void set_header_(const HgHeader&)
//   to give derived class enough freedom to compose the header which will be 
//   prepended to the data stream. (see hgheader.h)
//   NOTE: set_header_() may be called only when the thing is still under contruction 
//   (i.e. state_ == NOTYETRUNNING)
// 
// - void init_()
//   must be called by derived classes to start listening. doesn't do anything if 
//   state_ is not NOTYETRUNNING.
// 
// - virtual int read_() = 0
// - const int max_read_
// - char* data_
//   it is desired that the function read_() be used in derived classes to 
//   read the contents of the document. 
//   data_ is a char array of size max_read_ and holds the piece of data read 
//   by the function. the function returns the amount of bytes read successfully 
//   or -1 on error. if 0 bytes were read, this denotes a regular eof, and the 
//   connection to the server is closed.
//   
// 
// Description:      
// 
// Derived classes set up the source to read from, set the appropriate state 
// (set_state_()), and call init_() to get the thing working.
// init_() opens a port which will be connected by the document server if 
// everything works fine. The object then will send out the data provided by 
// derived classes onto the accepted connection. These actions take place within 
// the Dispatcher::instance() dispatch loop, SO NOTHING WILL HAPPEN WHEN NO SUCH 
// LOOP IS RUNNING OUTSIDE. 
// On errors the appropriate error state is set.
// 
// The data are provided by derived classes by means of the function read_(), see 
// there for description of correct usage.
// 
// A Header (class Hgheader, see there) is set only when set explicitly, either on 
// construction, or later by set_header_().
// 
// </class> 
class DcSender : public IOHandler {
public:
   static const int def_maxread ;
   static const long def_timeout ;

   enum DcSState {
      // ok
      NOTYETRUNNING,
      RUNNING,
      FINISHED,

      // error
      NOOPEN,
      NOREAD,
      NOPORT,
      NOACCEPT,
      CLOSED,
      FATAL,
      TIMEDOUT
   } ;

   DcSender (int port, int size, long timeout) ;
   DcSender (const HgHeader&, int port, int size, long timeout) ;
   virtual ~DcSender() ;

   const HgHeader& header() const { return hdr_; }

   int port() const ;

   boolean ok() const ;
   operator void*() const { return (void*)ok(); }
   boolean operator !() const { return !ok(); }

   DcSState state() const { return state_; }

   virtual int inputReady (int) ;
   virtual int outputReady (int) ;
   virtual void timerExpired (long sec, long usec) ;

protected:
   const int max_read_ ;
   char* data_ ;

protected:
   void set_state_(DcSState s) { state_ = s; }
   void set_header_(const HgHeader& h) ;
   boolean listen_() ;
   virtual int read_() = 0 ;

private:
   // an explanation of this see in the .C file at inputReady(), outputReady()
   boolean unlink_on_output_ready_ ;

   rpcbuf* port_ ;
   int portno_ ;

   int sockno_ ;
   RString rest_ ; // the rest of the prev. writing operation

   DcSState state_ ;

   long timeout_ ;
   boolean timer_set_ ;

   HgHeader hdr_ ;
   boolean first_time_ ;

private:
   void delete_port_() ;
   void delete_socket_() ;

   void start_timer_() ;
   void stop_timer_() ;
   void reset_timer_() ;

   DcSender (const DcSender&) : max_read_(-1) {}
   DcSender& operator = (const DcSender&) { return *this; }
} ;

// <class>
//  
// Name:             DcFileSender
// 
// Purpose:          send contents of a file 
// 
// Public Interface: 
// 
// - DcFileSender (int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   do nothing.
//   the source must be set later using init()
// 
// - DcFileSender (const HgHeader&, 
//                 int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   as above, but remembers that a header is wanted when getting the source at init(),
//   and determines the size then, if not yet set in the HgHeader.
// 
// - DcFileSender (const char* filename, 
//                 int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   Open a file with filename and send out the contents.
//   No header information is sent, so the server cannot tell if the data are correct.
//   (Handle with care.)
// 
// - DcFileSender (const HgHeader&, 
//                 const char* filename, 
//                 int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   Open a file with filename and send out the contents.
//   If the size information of the header is not yet initialized 
//   (h.size()==HgHeader::notinit), it will be set to be the size of the file.
// 
// - DcFileSender (ifstream*, 
//                 int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   As the filename constructor, but reads from the ifstream passed.
//   The ifstream isnt deleted on destruction.
// 
// - DcFileSender (const HgHeader&,
//                 ifstream*, 
//                 int port=0, 
//                 int maxread=DcSender::def_maxread, 
//                 long timeout=DcSender::def_timeout)
//   blah
// 
// - ~DcFileSender()
//   destructor
// 
// Description: 
// 
// none
// 
// </class> 
class DcFileSender : public DcSender {
public:
   DcFileSender (int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   DcFileSender (const HgHeader&,
                 int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   DcFileSender (const char* filename, 
                 int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   DcFileSender (const HgHeader&, 
                 const char* filename, 
                 int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   DcFileSender (ifstream*, 
                 int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   DcFileSender (const HgHeader&, ifstream*, 
                 int port = 0, 
                 int maxread = DcSender::def_maxread, 
                 long timeout = DcSender::def_timeout) ;
   virtual ~DcFileSender() ;

   boolean init (const char* filename) ;
   boolean init (ifstream*) ;

protected:
   virtual int read_() ;

private:
   ifstream* file_ ;
   boolean delete_ ;
   boolean read_yet_ ;
   boolean want_header_ ;
} ;

// <class> 
//  
// Name:             DcStringSender
// 
// Purpose:          send a (non-)null terminated string
// 
// Public Interface: 
// 
// - DcStringSender (const char*, int length=-1, 
//                   int port=0, 
//                   int maxread=DcSender::def_maxread, 
//                   long timeout=DcSender::def_timeout)
//   If length == -1 (or <0 anyway), the string is assumed null terminated.
//   No header sent (so handle with care.)
// 
// - DcStringSender (const HgHeader&, 
//                   const char*, int length=-1,
//                   int port=0, 
//                   int maxread=DcSender::def_maxread, 
//                   long timeout=DcSender::def_timeout)
//   If length < 0, and if the headers size is set, the strings size is 
//   assumed to be that size; if the headers size isnt set either, the
//   string is assumed to be null terminated, and both the headers size
//   field and the size of the data to be sent are set to the strings 
//   length.
//   If length >= 0, and the headers size field isnt set, that will will 
//   be adjusted; if the headers field is set additionally, it must equal 
//   length.
// 
// - DcStringSender (const RString&,
//                   int port=0, 
//                   int maxread=DcSender::def_maxread, 
//                   long timeout=DcSender::def_timeout)
//   As the first constructor with length specified.
// 
// - DcStringSender (const HgHeader&, 
//                   const RString&,
//                   int port=0, 
//                   int maxread=DcSender::def_maxread, 
//                   long timeout=DcSender::def_timeout)
//   As the second constructor with length specified.
// 
// - ~DcStringSender()
// 
// Description:      
// 
// none
// 
// </class> 
class DcStringSender : public DcSender {
public:
   DcStringSender (const char*, int length = -1, 
                   int port = 0, 
                   int maxread = DcSender::def_maxread,
                   long timeout = DcSender::def_timeout) ;
   DcStringSender (const HgHeader&, 
                   const char*, int length = -1,
                   int port = 0, 
                   int maxread = DcSender::def_maxread,
                   long timeout = DcSender::def_timeout) ;
   DcStringSender (const RString&,
                   int port = 0, 
                   int maxread = DcSender::def_maxread,
                   long timeout = DcSender::def_timeout) ;
   DcStringSender (const HgHeader&, 
                   const RString&,
                   int port = 0, 
                   int maxread = DcSender::def_maxread,
                   long timeout = DcSender::def_timeout) ;
   virtual ~DcStringSender() ;

protected:
   virtual int read_() ;
   const char* str_ ;
   const char* pos_ ;
   int length_ ;

   // for the third and fourth ctor ("remember the RString")
   RString rstr_ ;
} ;

#endif
