#include <stdlib.h>
#include <stdio.h>

#include "longlong.h"
#include "sysfuncs.h"
#include "memlib.h"

#include "bitio_m_abstract.h"
#include "bitio_m_random.h"

random_bitio_buffer::random_bitio_buffer(FILE *f, mg_u_long length)
{
  buffer = NULL;
  attachFile (f, length);
}

random_bitio_buffer::~random_bitio_buffer() {
  if (buffer != NULL) delete []buffer;
}

void random_bitio_buffer::attachFile (FILE *f, mg_u_long length) {
  // delete the old buffer
  if (buffer != NULL) {
    delete []buffer;
    buffer = NULL;
  }

  // reset all the values
  file   = f;
  pos    = 0;
  base   = 0;
  used   = 0;
  len    = length - 1;
  shift  = 0;

  // make sure the length is a power of 2
  while (len != 0) {
    ++shift;
    len = len >> 1;
  }
  len = 1 << shift;

  // allocate a new buffer
  buffer = new u_char[len];

  // fill up the buffer
  if (file != NULL) {
    fseek(file, 0, 0);
    size_t numbytes = fread(buffer, 1, len, file);
    
    if (len != numbytes){
#ifdef DEBUG
      fprintf(stderr, "[mgpp/lib/bitio_m_random.cpp L52] number of bytes read by fread does not match the requested amount\n");
#endif
    }
  }
}

random_bitio_buffer &random_bitio_buffer::operator= (const random_bitio_buffer &_rbb) {
  // delete the old buffer
  if (buffer != NULL) {
    delete []buffer;
    buffer = NULL;
  }

  file = _rbb.file;

  base = _rbb.base;
  used = _rbb.used;
  pos = _rbb.pos;
  len = _rbb.len;
  shift = _rbb.shift;

  // copy the buffer
  buffer = new u_char [len];
  //  memcpy (buffer, _rbb.buffer, len);
memcpy (buffer, _rbb.buffer, len);
  
  return (*this);
}

void random_bitio_buffer::error() {
}

void random_bitio_buffer::writeRead()
{
  if (used)
    {
      fseek(file, base, 0);
      fwrite(buffer, 1, len, file);
    }
  //  else
  //    {
      base += len;
      fseek(file, base, 0);
      size_t numbytes = fread(buffer, 1, len, file);
      if (numbytes != len)
	{
#ifdef DEBUG
	  fprintf(stderr, "[mgpp/lib/bitio_m_random.cpp L97] number of bytes read by fread does not match the requested amount\n");
#endif
	}
      pos = 0;
      used = 0;
  //    }
}

void random_bitio_buffer::encodeBit(int bit)
{
  do
    {
      if (bit != 0)
	{
	  buffer[pos >> 3] |= 0x80 >> (pos & 0x07);
	}
      else
	{
	  buffer[pos >> 3] &= 0xff7f >> (pos & 0x07);
	}
      used = 1;
      ++pos;

      if ((pos >> 3) >= len)
	{
	  writeRead();
	}
    }
  while (0);
}

void random_bitio_buffer::flush()
{
  if (used != 0)
    {
      fseek(file, base, 0);
      fwrite(buffer, 1, len, file);
      used = 0;
    }
}

void random_bitio_buffer::encodeDone()
{
  flush();
  if (buffer != NULL) {
    delete []buffer;
    buffer = NULL;
  }
}

mg_u_long random_bitio_buffer::tell()
{
  return ((base << 3) + pos);
}

void random_bitio_buffer::seek(mg_u_long toPos)
{
  if (((toPos >> 3) >= base) && (((toPos+7) >> 3) < (base + len)))
    {
      pos = toPos - (base << 3);
    }
  else
    {
      flush();
      base = toPos >> 3;
      base = (toPos >> (shift + 3)) << shift;
      fseek(file, base, 0);
      size_t numbytes = fread(buffer, 1, len, file);
      if (numbytes != len)
	{
#ifdef DEBUG
	  fprintf(stderr, "[mgpp/lib/bitio_m_random.cpp L166] number of bytes read by fread does not match the requested amount\n");
#endif
	}
      pos = toPos & 0x07;
      pos = toPos & ((8 << shift) - 1);
    }
}

int random_bitio_buffer::bit()
{
  //  fprintf(stderr, "Bit in");
  ++pos;
  if (((pos - 1) >> 3) >= len)
    {
      writeRead();
      pos = 1;
    }
  return (buffer[(pos - 1) >> 3] & (0x80 >> ((pos - 1) & 0x07))) != 0;
}

mg_s_long random_bitio_buffer::addff(mg_s_long b)
{
  return b + b + bit();
}

mg_s_long random_bitio_buffer::add00(mg_s_long b)
{
  return addff(b);
}

int random_bitio_buffer::bitOffset()
{
  return 0;
}

void random_bitio_buffer::done()
{
  encodeDone();
}


#ifdef USE_LONG_LONG
void random_bitio_buffer::seek_LL(mg_ullong toPos)
{
  if (((toPos >> 3) >= (mg_ullong) base) &&
      (((toPos + 7) >> 3) < (mg_ullong) (base + len)))
    {
      pos = (mg_s_long) (toPos - (mg_ullong) (base << 3));
    }
  else
    {
      flush();
      base = (mg_s_long) ((toPos >> (shift + 3)) << shift);

      fseek(file, base, 0);
      size_t numbytes = fread(buffer, 1, len, file);
      if (numbytes != len)
	{
#ifdef DEBUG 
	  fprintf(stderr, "[mgpp/lib/bitio_m_random.cpp L223] number of bytes read by fread does not match the requested amount\n");
#endif
	}
      pos = (mg_s_long) (toPos & ((8 << shift) - 1));
    }
}

mg_ullong random_bitio_buffer::tell_LL()
{
  return (((mg_ullong) base) << 3ull) + pos;
}
#endif

