/**************************************************************************
 *
 * FText.cpp -- File structures for text compression
 * Copyright (C) 1999  Rodger McNab
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************/

#include "FText.h"
#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */


BitAddr::BitAddr () {
  Clear ();  
}

void BitAddr::Clear () {
  byte = 0;
  bit = 0;
}

bool BitAddr::Read (FILE *f) {
  return (ReadUL (f, byte) && ReadUC (f, bit));
}

bool BitAddr::Write (FILE *f) const {
  return (WriteUL (f, byte) && WriteUC (f, bit));
}

#define BITADDRLEN (sizeof(mg_u_long) + sizeof(unsigned char))

  
TextLevelInfo::TextLevelInfo () {
  Clear();
}

void TextLevelInfo::Clear () {
  levelTag.erase (levelTag.begin(), levelTag.end());
  textIdxPtr = 0;
  numEntries = 0;
}

bool TextLevelInfo::Read (FILE *f) {
  return (ReadUCArray (f, levelTag) &&
	  ReadUL (f, textIdxPtr) &&
	  ReadUL (f, numEntries));
}

bool TextLevelInfo::Write (FILE *f) const {
  return (WriteUCArray (f, levelTag) &&
	  WriteUL (f, textIdxPtr) &&
	  WriteUL (f, numEntries));
}

ostream &operator<<(ostream &s, const TextLevelInfo &l) {
  s << "  Tag: \"" << l.levelTag << "\"\n"
    << "  textIdxPtr: " << l.textIdxPtr << "\n"
    << "  numEntries: " << l.numEntries << "\n\n";
  
  return s;
}


FTextLevel::FTextLevel () {
  Clear();
}

void FTextLevel::Clear () {
  levelInfo.erase (levelInfo.begin(), levelInfo.end());
}

bool FTextLevel::Read (FILE *f) {
  // read in the array size
  mg_u_long arrSize = 0;
  if (!ReadVarLenUL (f, arrSize)) return false;

  TextLevelInfo thisLevel;
  while (arrSize > 0) {
    // read and insert the next level information
    if (!thisLevel.Read (f)) return false;
    levelInfo[thisLevel.levelTag] = thisLevel;
    
    --arrSize;
  }

  return true;
}

bool FTextLevel::Write (FILE *f) const {
  // write out the array size
  if (!WriteVarLenUL (f, levelInfo.size())) return false;

  // write out each level info
  TextLevelInfoMap::const_iterator here = levelInfo.begin();
  TextLevelInfoMap::const_iterator end = levelInfo.end();
  while (here != end) {
    if (!(*here).second.Write (f)) return false;
    
    ++here;
  }

  return true;
}

ostream &operator<<(ostream &s, const FTextLevel &l) {
  TextLevelInfoMap::const_iterator here = l.levelInfo.begin ();
  TextLevelInfoMap::const_iterator end = l.levelInfo.end ();
  while (here != end) {
    s << (*here).second;
    ++here;
  }
  
  return s;
}


TextIdx::TextIdx () {
  Clear ();
}

void TextIdx::Clear () {
  start.Clear();
  end.Clear();
  which = 0;
}

#define TEXTIDXLEN (BITADDRLEN*2 + sizeof(char))

bool TextIdx::Read (FILE *f, const TextLevelInfo &levelInfo,
		    mg_u_long docNum) {
  if (!SeekTextIdx (f, levelInfo, docNum)) return false;
  return Read (f);
}

bool TextIdx::Read (FILE *f) {
  return (start.Read (f) && end.Read (f) && ReadUC (f, which));
}

bool TextIdx::Write (FILE *f) const {
  return (start.Write (f) && end.Write (f) && WriteUC (f, which));
}

ostream &operator<<(ostream &s, const TextIdx &t) {
  s << "start byte: " << t.start.byte << "\n";
  s << "start bit:  " << (int)t.start.bit << "\n";
  s << "end byte:   " << t.end.byte << "\n";
  s << "end bit:    " << (int)t.end.bit << "\n";
  s << "which:      " << (int)t.which << "\n\n";
  
  return s;
}


bool SeekTextIdx (FILE *f, const TextLevelInfo &levelInfo,
		  mg_u_long docNum) {
  if (docNum == 0 || docNum > levelInfo.numEntries) return false;
  
  mg_u_long seekPos = levelInfo.textIdxPtr + (docNum-1) * TEXTIDXLEN;
  if (fseek (f, seekPos, SEEK_SET) != 0) return false;
  
  return true;
}


bool ReadTextIdxArray (FILE *f, TextIdxArray &a, mg_u_long arrSize) {
  // clear the array
  a.erase (a.begin(), a.end());

  // read in each element
  TextIdx idx;
  while (arrSize > 0) {
    if (!idx.Read(f)) return false;
    a.push_back (idx);
    --arrSize;
  }

  return true;
}

bool WriteTextIdxArray (FILE *f, const TextIdxArray &a) {
  TextIdxArray::const_iterator here = a.begin();
  TextIdxArray::const_iterator end = a.end();
  while (here != end) {
    if (!(*here).Write(f)) return false;
    ++here;
  }
  
  return true;
}


CompressTextInfo::CompressTextInfo () {
  Clear();
}

void CompressTextInfo::Clear () {
  ResetStart();
  docPtrs.erase (docPtrs.begin(), docPtrs.end());
}

void CompressTextInfo::ResetStart () {
  inDoc = false;
  start.Clear();
  which = 0;
}
  
void CompressTextInfo::SetStart (mg_u_long startPos,
				 unsigned char startBit,
				 unsigned char startWhich) {
  // place an imaginary end tag if needed
  // note: the document tag always needs a closing tag
  if (inDoc) SetEnd (startPos, startBit);

  // remember start of level
  inDoc = true;
  start.byte = startPos;
  start.bit = startBit;
  which = startWhich;
}

void CompressTextInfo::SetEnd (mg_u_long endPos,
			       unsigned char endBit) {
  if (inDoc) {
    // add completed entry to list of ptrs
    TextIdx textIdx;
    textIdx.start = start;
    textIdx.end.byte = endPos;
    textIdx.end.bit = endBit;
    textIdx.which = which;
    docPtrs.push_back (textIdx);
    
    // reset this entry
    ResetStart();
  }
}

