// 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:        hgheader.h
// 
// Purpose:     header sent with documents from/to the document server
// 
// Created:     27 Oct 93   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// 
// </file> 
#ifndef hg_hyperg_hgheader_h
#define hg_hyperg_hgheader_h

#include <hyperg/utils/buffer.h>
#include <hyperg/utils/inetaddr.h>
#include <hyperg/utils/str.h>
#include <hyperg/utils/types.h>


// <class> 
//  
// Name:             HgHeader
// 
// Purpose:          
// 
// Public Interface: 
// 
// - HgHeader()
//   default constructor 
// 
// - HgHeader (const HgHeader&)
//   copy constructor
// 
// - int read (const char*, int length)
// - int read (const RString&)
// 
//   fills this with the contents of a char array of size length or RString, resp..
//   returns the actual size of the header contained in the array, else -1.
// 
// - void write (RString&) const
// 
//   formats this into an RString in the same way it is read by the above method.
// 
// - boolean ok() const
//   boolean operator !() const
//   operator void*() const
// 
//   check the state of this
// 
// - HgHdrType type() const
//   long size() const
//   long refno() const
//   boolean compressed() const
//   int version() const
// 
//   blah
// 
// - HgHeader& type (HgHdrType)
//   HgHeader& size (long)
//   HgHeader& refno (long)
//   HgHeader& compressed (boolean c)
// 
//   set the members. 
//   as they return *this, you may say "h.size(100).type(HgHeader::HTF)"
// 
// - static const int maxsize
//   static const int minsize
// 
//   (max|min)imal size of a formatted HgHeader. 
// 
// - static const char terminator
// 
//   terminating char of a formatted HgHeader (currently a '\0', but may be changed)
// 
// 
// Description:      
// 
// Writing a HgHeader:
// 
// Make an instance with the non-default constructor and format it into an RString, 
// which you may then write onto anything you want (socket?).
// 
// Reading a HgHeader:
// 
// Make an instance with the default constructor. Then read minsize<=x<=maxsize 
// bytes from the medium you want to read the header from (but only maxsize is 
// guaranteed to work!). Finally use the read() method to fill (parse) the contents of the 
// header from that char array.
// 
// </class> 
class HgHeader {
public:
   enum HgHdrType {
      HTF,
         PLAINTEXT,
         HTML,
         IMAGE,
         MOVIE,
         SCENE,
         AUDIO,
         UNKNOWN,
         POSTSCRIPT,
         NTYPES
         } ;
   enum Def { Default } ;

public:
   HgHeader() ;
   HgHeader (Def) ;
   HgHeader (const HgHeader&) ;

   HgHeader& operator = (const HgHeader&) ;

   int read (char*, int length) ; // the HgRegexp wants it non-const
   int read (const RString&) ;
   // size will include the terminating 0-char:
   void write (char*, int& size) const ;
   void write (RString&) const ;

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

   HgHdrType type() const { return type_; }
   long size() const { return size_; }
   long refno() const { return refno_; }
   boolean compressed() const { return compressed_; }
   int version() const { return usedversion_; }

   HgHeader& type (HgHdrType t) { type_ = t; return *this; }
   HgHeader& size (long s) { size_ = s; return *this; }
   HgHeader& refno (long r) { refno_ = r; return *this; }
   HgHeader& compressed (boolean c) { compressed_ = c; return *this; }

   // can't send out 'notinit' values since old versions (probably 
   // inside old servers and clients) will report an error on reading 
   // such values.
   // one doesn't have to call it explicitly before writing out the header, 
   // HgHeader::write() does it by itself.
   void setDefaults() ;

   static boolean possiblePrefix (const RString&) ;

   static const int maxsize ;
   static const int minsize ;
   static const char terminator ;
   static const RString tag ;
   static const int notinit ;

private:
   // THE contents
   HgHdrType type_ ;
   long size_ ; // size of the data that follow
   long refno_ ; // reference number 
   int compressed_ ;
   int usedversion_ ;
   // 

   static const int version_ ;
   static const char* format_ ;

private:
   void nil_out_() ;
   boolean is_nil_() const ;
   static int get_line_(const char* begin, const char* end) ;
} ;

inline int HgHeader :: read (const RString& s) {
   return read ((char*)s.string(), s.length()) ;
}

class HWDcHeader {
public:
   enum State {
     INIT,
     WSPRE,
     H1,
     G,
     H2,
     D,
     R,
     WSAFT,
     CRB,
     LFB,
     WS0,
     TAG,
     WS1,
     EQ,
     WS2,
     VAL,
     WS3,
     CR,
     LF,
     TERM,
     MYOWNERROR // there are some global-name-space-pollutors around
   } ;

   enum Def { Default } ;

public:
   HWDcHeader() ; // not initialized
   HWDcHeader (Def) ; //  default values
   HWDcHeader (const HWDcHeader&) ;

   bool ok() const { return state_!=MYOWNERROR; }
   bool complete() const { return state_==TERM; }

   long size() const { return size_; }
   long refno() const { return refno_; }
   const INETAddress& yourTCP() const { return your_tcp_; }
   const INETAddress& myTCP() const { return my_tcp_; }

   HWDcHeader& size (long) ;
   HWDcHeader& refno (long) ;
   HWDcHeader& yourTCP (const INETAddress&) ;
   HWDcHeader& myTCP (const INETAddress&) ;

   // returns number of characters sucessfully accepted. these will
   // then be part of the header data. nevertheless, you have to check
   // the state of this afterwards to determine if
   // ready/error/wantmore.
   // NOTE: even if there was an error the number of successfully
   // accepted chars is returned (up to, but not including, the
   // erroneous one). this enables you to determine where the error
   // occured.
   int add (const char*, int) ;

   // false if error (this not ok() anymore). true if the char was
   // accepted; i.e., added to my data. if true, my state may be
   // "complete" or "no error and want more"; user has to check this
   // afterwards.
   bool add (char) ;

   void write (OBuffer&) const ;

private:
   // needed for backward compatiblity with the above HgHeader
   HgHeader::HgHdrType type_ ;
   int compressed_ ;
   int usedversion_ ;

   // real ones
   long size_ ; // size of the data that follow
   long refno_ ; // reference number 
   INETAddress your_tcp_ ;
   INETAddress my_tcp_ ;

   // used while parsing, nilled out afterwards (entering TERM or
   // MYOWNERROR)
   OBuffer tag_ ;
   OBuffer value_ ;
   
   void set_() ; // ... a member out of the tag_/value_
   void term_() ; // ... hmmm

   // and, of course, ...
   State state_ ;

   // some utilities
   static bool istag_(char) ;
   static bool isvalue_(char) ;

   static bool is_tcp_(const RString&, INETAddress&) ;

public:
   static const char* version4 ;
} ;
static const char* hg_hyperg_hwdcheader_version = HWDcHeader::version4 ;

#endif
