#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include "giflib.h"
#include "util.h"
#include "codetab.h"
#include "myerror.h"

extern jmp_buf jmpbuffer;

void ct_preinitialize (codetab* ct) {
  unsigned int initialpower;
  int initialsize;

  initialpower = 0;
  initialsize  = ipow(2, initialpower) - CT_MALLOC_OVERHEAD;
  /* initialsize = (2^initialpower) - CT_MALLOC_OVERHEAD */
  
  ct->table = NULL;   /* this initialization is needed by ct_makeroomfor */
  ct->currentmax = 0; /* this too */
  ct->allocbytes = 0; /* this too */
  ct->allocpower = 0; /* this too */
  ct->nextentry  = 0; /* not needed but it is good to set here. */

  ct_makeroomfor(ct, initialsize);
} 

void ct_makeroomfor (codetab* ct, int elts) {
  int bytes_needed;
  int new_power;
  unsigned int new_bytes;
  if (elts > ct->currentmax) {
    new_bytes    = ct->allocbytes;
    new_power    = ct->allocpower;
    bytes_needed = elts * sizeof(CT_ELT_TYPE);

    while ( ((int) new_bytes ) < bytes_needed) {
      new_power++;
      new_bytes = (unsigned int) ipow(2, new_power) - CT_MALLOC_OVERHEAD;
    }
    
    ct->allocpower = new_power;
    ct->allocbytes = (unsigned int) new_bytes;
    ct->currentmax = new_bytes / sizeof(CT_ELT_TYPE);
    if (ct->table == NULL) {
      ct->table = malloc(ct->allocbytes);
    } else {
      ct->table = realloc(ct->table, ct->allocbytes);
    }
    if (ct->table == NULL) {
      /* FAILURE!!!!! */
      longjmp(jmpbuffer, G_ERR_NOMEM);
    }
  }
}

#define ct_setcar(ct, entrynum, x) ((ct)->table)[(entrynum)].car = (x)
#define ct_setcdr(ct, entrynum, x) ((ct)->table)[(entrynum)].cdr = (x)
#define ct_setlen(ct, entrynum, x) ((ct)->table)[(entrynum)].len = (x)


int ct_append (codetab* ct, int car, int cdr) {
  int answer;
  /* returns index of new codetable entry */

  if (ct->nextentry > ct->currentmax) {
    ct->allocpower = ct->allocpower++;
    ct->allocbytes = ((ct->allocbytes * 2) + CT_MALLOC_OVERHEAD);
    ct->currentmax = ct->allocbytes / sizeof(CT_ELT_TYPE);

    ct->table = realloc(ct->table, ct->allocbytes);
    /* deal with any errors */
    if (ct->table == NULL) {
      /* FAILURE!!!!! */
      longjmp(jmpbuffer, G_ERR_NOMEM);
    }
  }      

  ct_setcar(ct, ct->nextentry, car);
  ct_setcdr(ct, ct->nextentry, cdr);
  if (car == -1) {
    ct_setlen(ct, ct->nextentry, 1L);
  } else {
    ct_setlen(ct, ct->nextentry, (ct->table[car].len + 1));
  }

  answer = ct->nextentry;
  ct->nextentry++;
  return(answer); /* success */
}

void ct_seed(codetab *ct, int ct_length) {
  int i;

  ct_makeroomfor(ct, (ct_length + 2));

  for (i = 0; (i < ct_length) ; i++) {
    ct_setcar(ct, i, -1);
    ct_setcdr(ct, i,  i);
    ct_setlen(ct, i,  1);
    /* these entries have no "preceding" codes;  */
    /* they only a single code, their own number */
  }

  ct_setcar(ct, (ct_length    ), -1); /* clearcode */
  ct_setcdr(ct, (ct_length    ), -1); /* clearcode */
  ct_setlen(ct, (ct_length    ), 0L); /* clearcode */
  ct_setcar(ct, (ct_length + 1), -1); /* endofinfo */
  ct_setcdr(ct, (ct_length + 1), -1); /* endofinfo */
  ct_setlen(ct, (ct_length + 1), 0L); /* endofinfo */

  ct->nextentry = (ct_length + 2);
}
