/*
 *  This file handles filling of the buffer and contains some additional utility routines.
 */

#include "video.h"
#include "proto.h"
#include "util.h"

/* Declarations of global variables used. */

int bitOffset;
int bufLength;
unsigned int *bitBuffer;


/*
 *--------------------------------------------------------------
 *
 * correct_underflow --
 *
 *	Called when buffer does not have sufficient data to 
 *      satisfy request for bits.
 *      Calls get_more_data, an application specific routine
 *      required to fill the buffer with more data.
 *
 * Results:
 *      None really.
 *  
 * Side effects:
 *	buf_length and buffer fields in curVidStream structure
 *      may be changed.
 *
 *--------------------------------------------------------------
 */

void correct_underflow(void)
{
  int status;

  status = get_more_data(curVidStream->buf_start, curVidStream->max_buf_length, &bufLength, &bitBuffer);

  if (status  < 0) {
    if (!quietFlag) {
      fprintf (stderr, "\n");
      perror("Unexpected read error.");
    }
    exit(1);
  }
  else if ((status == 0) && (bufLength < 1)) {
    if (!quietFlag) {
      fprintf(stderr, "\nImproper or missing sequence end code.\n");
    }
#ifdef ANALYSIS
    PrintAllStats();
#endif
    if (!quietFlag) {
      PrintTimeInfo();
    }

    if (loopFlag) longjmp(env, 1);
    DestroyVidStream(curVidStream);
    exit(0);
  }

}


/*
 *--------------------------------------------------------------
 *
 * get_ext_data --
 *
 *	Assumes that bit stream is at begining of extension
 *      data. Parses off extension data into dynamically 
 *      allocated space until start code is hit. 
 *
 * Results:
 *	Pointer to dynamically allocated memory containing
 *      extension data.
 *
 * Side effects:
 *	Bit stream irreversibly parsed.
 *
 * Niceness -10000 !!!!
 *
 *--------------------------------------------------------------
 */

char *get_ext_data (void)
{
  int size, marker;
  char *dataPtr;
  unsigned int data;

  /* Set initial ext data buffer size. */

  size = EXT_BUF_SIZE;

  /* Allocate ext data buffer. */

  dataPtr = (char *) malloc(size);

  /* Initialize marker to keep place in ext data buffer. */

  marker = 0;

  /* While next data is not start code... */
  while ((show_bitsn(24,data)) != 0x000001) {				/* data re-usage could be applied ! */

    /* Put ext data into ext data buffer. Advance marker. */

    get_bits8( dataPtr[marker] );
    marker++;

    /* If end of ext data buffer reached, resize data buffer. */

    if (marker == size) {
      size += EXT_BUF_SIZE;
      dataPtr = (char *) realloc(dataPtr, size);
    }
  }

  /* Realloc data buffer to free any extra space. */

  dataPtr = (char *) realloc(dataPtr, marker);

  /* Return pointer to ext data buffer. */

  return dataPtr;
}


/*
 *--------------------------------------------------------------
 *
 * next_start_code --
 *
 *	Parses off bitstream until start code reached. When done
 *      next 4 bytes of bitstream will be start code. Bit offset
 *      reset to 0.
 *
 * Results:
 *	Status code.
 *
 * Side effects:
 *	Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

int next_start_code(void)
{
  int state;
  int byteoff;
  unsigned int data;

  /* If no current stream, return error. */

  if (curVidStream == NULL) return NO_VID_STREAM;

  /* If insufficient buffer length, correct underflow. */

  if (bufLength < 2) {
    correct_underflow();
  }

  /* If bit offset not zero, reset and advance buffer pointer. */

  byteoff = bitOffset & 7;

  if (byteoff != 0) {
    flush_bits((8-byteoff));
  }

  /* Set state = 0. */

  state = 0;

  /* While buffer has data ... */

  while(bufLength > 0) {

    /* If insufficient data exists, correct underflow. */

    if (bufLength < 2) {
      correct_underflow();
    }

    /* If next byte is zero... */

    get_bits8(data);

    if (data == 0) {
      if (state < 2) state++;			/* If state < 2, advance state. */
    }
    /* If next byte is one... */
    else if (data == 1) {
      if (state == 2) state++;			/* If state == 2, advance state (i.e. start code found). */
      /* Otherwise, reset state to zero. */
      else state = 0;
    }
    /* Otherwise byte is neither 1 or 0, reset state to 0. */
    else {
      state = 0;
    }
    if (state == 3) {				/* If state == 3 (i.e. start code found)... */

      /* Set buffer pointer back and reset length & bit offsets so
	 next bytes will be beginning of start code. 
      */

      bitOffset -= 24;

#ifdef ANALYSIS
      bitCount -= 24;
#endif

      if (bitOffset < 0) {
	bitOffset += 32;
	bufLength++;
	bitBuffer--;
      }

      /* Return success. */

      return OK;
    }
  }

  /* Return underflow error. */

  return UNDERFLOW;
}


/*
 *--------------------------------------------------------------
 *
 * get_extra_bit_info --
 *
 *	Parses off extra bit info stream into dynamically 
 *      allocated memory. Extra bit info is indicated by
 *      a flag bit set to 1, followed by 8 bits of data.
 *      This continues until the flag bit is zero. Assumes
 *      that bit stream set to first flag bit in extra
 *      bit info stream.
 *
 * Results:
 *	Pointer to dynamically allocated memory with extra
 *      bit info in it. Flag bits are NOT included.
 *
 * Side effects:
 *	Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

char *get_extra_bit_info (void)
{
  int size, marker;
  char *dataPtr;
  unsigned int data;

  /* Get first flag bit; if flag is false, return NULL pointer (i.e. no extra bit info). */

  if (!(get_bits1(data))) return NULL;

  /* Initialize size of extra bit info buffer and allocate. */

  size = EXT_BUF_SIZE;
  dataPtr = (char *) malloc(size);

  /* Reset marker to hold place in buffer. */

  marker = 0;

  /* While flag bit is true. */

  while (data) {

    /* Place in extra bit info buffer. */

    get_bits8( dataPtr[marker] );
    marker++;

    /* If buffer is full, reallocate. */

    if (marker == size) {
      size += EXT_BUF_SIZE;
      dataPtr = (char *) realloc(dataPtr, size);
    }

    /* Get next flag bit. */
    get_bits1(data);
  }

  /* Reallocate buffer to free extra space. */

  dataPtr = (char *) realloc(dataPtr, marker);

  /* Return pointer to extra bit info buffer. */

  return dataPtr;
}
