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

#include "assert.h"

#include <hyperg/utils/str.h>

#include <InterViews/regexp.h>

#include <strstream.h>
#include <stdio.h>
#include <string.h>




#define VERBOSE
#include "verbose.h"




// --------------------------------------------------------------------
const int HgHeader::maxsize = 1024 ;
const int HgHeader::minsize = 7 ; // the empty header: HGHDR\n\0
const char HgHeader::terminator = '\0' ;
const RString HgHeader::tag ("HGHDR") ;
const int HgHeader::notinit = -100 ;

const int HgHeader::version_ = 1 ;
const char* HgHeader::format_ = "%s\nver=%d\ntype=%d\nsz=%ld\nref=%ld\nZ=%d\n" ;

HgHeader :: HgHeader() {
   nil_out_() ;
}

HgHeader :: HgHeader (Def) {
   nil_out_() ;
   setDefaults() ;
}

HgHeader :: HgHeader (const HgHeader& h) {
   operator = (h) ;
}

HgHeader& HgHeader :: operator = (const HgHeader& h) {
   type_ = h.type_ ;
   size_ = h.size_ ;
   refno_ = h.refno_ ;
   compressed_ = h.compressed_ ;
   usedversion_ = h.usedversion_ ;
   return *this ;
}

int HgHeader :: read (const char* s, int length) {
   static Regexp re_size ("^sz=(-?[0-9]+)$") ;
   static Regexp re_refno ("^ref=(-?[0-9]+)$") ;
   static Regexp re_type ("^type=([0-9]+)$") ;
   static Regexp re_compressed ("^Z=([0,1])$") ;
   static Regexp re_usedversion ("^ver=([0-9]+)$") ;

   nil_out_() ;
   
   if (length < minsize)
      return -1 ;

   const char* run = s ;
   const char* end = s + length ;
   int linelen ;

   if ((linelen = get_line_(run, end)) <=0)
      return -1 ;
   if (tag != RString(run,linelen))
      return -1 ;
   
   run += linelen + 1 ;

   while ((linelen = get_line_(run, end)) > 0) {
      if (0 <= re_size.Match (run, linelen, 0))
         size_ = ::atol (run + re_size.BeginningOfMatch(1)) ;
      else if (0 <= re_refno.Match (run, linelen, 0))
         refno_ = ::atol (run + re_refno.BeginningOfMatch(1)) ;
      else if (0 <= re_type.Match (run, linelen, 0)) {
         int t = ::atoi (run + re_type.BeginningOfMatch(1)) ;
         if (t>=0 && t<NTYPES)
            type_ = HgHdrType (t) ;
         else 
            type_ = NTYPES ;
      }
      else if (0 <= re_compressed.Match (run, linelen, 0))
         compressed_ = ::atoi (run + re_compressed.BeginningOfMatch(1)) ;
      else if (0 <= re_usedversion.Match (run, linelen, 0))
         usedversion_ = ::atoi (run + re_usedversion.BeginningOfMatch(1)) ;

      run += linelen + 1 ;
   }

   if (linelen < 0) return -1 ; // no header 

   return run - s + 1 ;
}

void HgHeader :: write (RString& s) const {
   static char tmp[maxsize] ;
   int size ;
   write (tmp, size) ;
   s = RString (tmp, size) ;
}

void HgHeader :: write (char* s, int& size) const {
   hgassert (ok(), "HgHeader::write(): this not ok") ;
   // have to set default values since old versions of HgHeader::read() 
   // wont let values of notinit pass through
   HgHeader h (*this) ;
   h.setDefaults() ;
   ::sprintf (s, format_, 
              tag.string(), h.version(), h.type(), h.size(), h.refno(), h.compressed()) ;
   size = strlen (s) + 1 ;
}

void HgHeader :: setDefaults() {
   if (type_ < 0  ||  type_ >= NTYPES)
      type_ = UNKNOWN ; // 
   if (size_ == notinit)
      // server only checks for >= 0, but that means: "is initialized"
      size_ = -1 ; 
   if (refno_ == notinit) 
      // as above
      refno_ = -1 ; 
   if (compressed_ == notinit)
      // not compressed by default
      compressed_ = 0 ; 
   if (usedversion_ == notinit) 
      usedversion_ = version_ ;
}

boolean HgHeader :: possiblePrefix (const RString& str) {
   const char* h = tag.string() ;
   const char* s = str.string() ;
   int len = (str.length() <= tag.length()) ?  str.length() :  tag.length() ;
   
   for (int i=0 ; i<len ; i++, s++, h++) 
      if (*s != *h)
         return false ;
   return true ;
}

int HgHeader :: get_line_(const char* s, const char* end) {
   hgassert (s<=end, "HgHeader::get_line_(): already passed end of data") ;
   if (*s == '\0')
      return 0 ;
   const char* run = s ;
   while (run < end) {
      if (*run == '\n') {
         return run-s ;
      }
      run++ ;
   }
   return -1 ;
}

void HgHeader :: nil_out_() {
   type_ = NTYPES ;
   size_ = notinit ;
   refno_ = notinit ;
   compressed_ = notinit ;
   usedversion_ = version_ ;
}

boolean HgHeader :: is_nil_() const {
   return type_ == NTYPES &&
          size_ == notinit && 
         refno_ == notinit && 
    compressed_ == notinit ;
}

