//***************************************************************************
// This source code is copyrighted 2002 by Google Inc.  All rights
// reserved.  You are given a limited license to use this source code for
// purposes of participating in the Google programming contest.  If you
// choose to use or distribute the source code for any other purpose, you
// must either (1) first obtain written approval from Google, or (2)
// prominently display the foregoing copyright notice and the following
// warranty and liability disclaimer on each copy used or distributed.
// 
// The source code and repository (the "Software") is provided "AS IS",
// with no warranty, express or implied, including but not limited to the
// implied warranties of merchantability and fitness for a particular
// use.  In no event shall Google Inc. be liable for any damages, direct
// or indirect, even if advised of the possibility of such damages.
//***************************************************************************


#include "goo-repos-reader.h"

#define MIN(a,b) ((a) > (b) ? (b) : (a))


ReposReader::ReposReader(std::istream* repos, const string& repos_name)
  : repos_(repos), repos_name_(repos_name), decode_buf_len_(0),
    decoder_(decode_buf_, 0), cur_pos_(0), chars_buf_(NULL) {
  ReallocCharsBuf(kReadBufSize);
  RefillBuf();
}

// Refill decode_buf_ from repos_, keeping the last "avail()" bytes in
// decode_buf_.  If there are not bytes to read, reports an error and
// exits.
void ReposReader::RefillBuf() {
  int keep = decoder_.avail();
  if (keep) {
    memmove(decode_buf_, &decode_buf_[decode_buf_len_ - keep], keep);
  }
  repos_->read(&decode_buf_[keep], sizeof(decode_buf_) - keep);
  if (repos_->gcount() == 0) {
    ParseError("ReposReader::RefillBuf: premature end of repository file "
               + repos_name_);
  }
  decode_buf_len_ = repos_->gcount() + keep;
  decoder_.reset(decode_buf_, decode_buf_len_);
}

// Make chars_buf_ big enough for size + 1 chars
void ReposReader::ReallocCharsBuf(int size) {
  delete[] chars_buf_;
  chars_buf_len_ = size + 1;
  chars_buf_ = new char[chars_buf_len_];
}


const char* ReposReader::ReadCharStar(int* len) {
  *len = ReadInt();
  return ReadCharsOnly(*len);
}

const char* ReposReader::ReadCharsOnly(int numchars) {
  int togo = numchars;

  // chars_buf_ has to be big enough for numchars + 1 chars
  if (numchars >= chars_buf_len_) ReallocCharsBuf(numchars);
  
  while (togo > 0) {
    if (decoder_.avail() == 0) {
      RefillBuf();
    }
    int n = MIN(togo, decoder_.avail());
    assert(n != 0);
    decoder_.getn(&chars_buf_[numchars - togo], n);
    togo -= n;
    cur_pos_ += n;
  }
  return chars_buf_;
}

uint32 ReposReader::ReadFixedUint32() {
  if (decoder_.avail() < 4) {
    RefillBuf();
  }
  cur_pos_ += 4;
  return decoder_.get32();
}

uint32 ReposReader::ReadVarUint32() {
  uint32 u;
  int n = decoder_.avail();
  if (! decoder_.get_varint32(&u)) {
    RefillBuf();
    n = decoder_.avail();
    if (! decoder_.get_varint32(&u)) {
      ParseError("ReposReader::ReadVarUint32: can't decode varint32");
    }      
  }
  cur_pos_ += n - decoder_.avail();
  return u;  
}

int ReposReader::ReadInt() {
  int i;
  int n = decoder_.avail();
  if (! decoder_.get_varsigned32(&i)) {
    RefillBuf();
    n = decoder_.avail();
    if (! decoder_.get_varsigned32(&i)) {
      ParseError("ReposReader::ReadInt: can't decode varsigned32");
    }      
  }
  cur_pos_ += n - decoder_.avail();
  return i;  
}

unsigned char ReposReader::ReadByte() {
  if (!decoder_.avail()) {
    RefillBuf();
  }
  cur_pos_ += 1;
  return decoder_.get8();
}
 
bool ReposReader::AtEnd() const {
  return (decoder_.avail() == 0) && repos_->eof();
}

void ReposReader::ParseError(const string& errmsg) {
  cerr << errmsg << ": repository " << repos_name_ 
       << ", position " << cur_pos_ << std::endl;
  exit(1); 
}

