#include "huff.h"
#ifdef PILOT 
  #include "huff_pilot.h"
#else
  #include "huff_pc.h"
#endif

static inline void setWeight(unsigned short node, unsigned long weight) {
  parent[node-256].weight = weight;
}

static inline void setUsed(unsigned short node, unsigned short used) {
  if (node >=256) 
    parent[node-256].used = used;
  else
    counter[node].used = used; 
}

static inline void setChildren(unsigned short node, unsigned short cLeft, 
  unsigned short cRight) {

  parent[node-256].lChild = cLeft;
  parent[node-256].rChild = cRight;
}

//returns the weight of a given node
static inline unsigned long weight(unsigned short node) {
  if (node >=256) return parent[node-256].weight;
  return counter[node].weight;
}

static inline short nodeIsUsed(short node) {
  if (node >=256) return parent[node-256].used;
  return counter[node].used;
}

//requires that children have been initialized with their weights
void buildTree() {
  unsigned short min1, min2,i;
  // clear old stuff out
  minUnusedParent = 256;
  setWeight(511, -1); // max value possible due to type conversion

  for(;;) {
    //find minimum2 values
    min1 = 511; min2 = 511;
    for (i=0; i<minUnusedParent; i++) {
      if (nodeIsUsed(i)) continue;
      if (weight(i) <weight(min1)) {
        min2 = min1;
        min1 = i;
      } else if (weight(i) < weight(min2)) {
        min2 = i;
      }
    }
    if (min2==511) break;
    // add to tree
    setWeight(minUnusedParent, weight(min1)+weight(min2) );
    setChildren(minUnusedParent, min1, min2);
    setUsed(minUnusedParent,0);
    setUsed(min1,1);
    setUsed(min2,1);
    //printf("%-3u <%-3u %-3u> %-5lu\n", 
    //  minUnusedParent, min1, min2, weight(minUnusedParent) );
    minUnusedParent++;
  }
}

void buildCode(unsigned short node, 
	unsigned long code, unsigned short length) {
  if (node < 256) {
    table[node].code = code;
    table[node].length = length;
//    if (isprint( (char) node) )
//      printf("Code for %c is %lu. Length %d\n", (char) node, code, length);
//    else
//      printf("Code for <%d> is %lu. Length %d\n", node, code, length);
    return;
  } 
  buildCode(parent[node-256].lChild, code*2, length+1);
  buildCode(parent[node-256].rChild, code*2+1, length+1);
}

//returns non-zero for bit present
static inline unsigned char getBit() {
  unsigned char returnVal; 
  if (inBitCount == 0) {
    inBitBuffer = getCompressedByte();
    inBitCount = 8;
  }
  returnVal = (inBitBuffer & 0x80 );
  inBitCount--;
  inBitBuffer<<=1;      
  return returnVal;
}

static void putBits(unsigned long data, unsigned char length) {
  if (length > 16) { 
    putBits(data >> 16, length - 16);
    putBits(data & 0xffff, 16);
    return; }
  outBitBuffer |= data <<(32 - length - numOutBits);
  numOutBits += length;
  while (numOutBits >= 8) {
    putCompressedByte(outBitBuffer >> 24);
    outBitBuffer <<=8;
    numOutBits -= 8;
  }
}

static void flushBits() {
  if (numOutBits)
    putBits(0, 8 - numOutBits);
}

void compress() {
  int c;
  for(;;) {
    c = getUncompressedByte();
    if (c == EOF) break;
    putBits(table[c].code, table[c].length);
  }
  putBits(table[255].code, table[255].length); // *****testing only
  flushBits(); 
}

void decompress() {
  short node;
  for(;;) {
    node = minUnusedParent-1;
    while (node >= 256) {
      if (getBit()) 
        node = parent[node&0xff].rChild;
      else
        node = parent[node&0xff].lChild;
    }
    if (node==255) break;
    putDecompressedByte(node);    
  }
}

#ifndef PILOT
  #include "huff_pc_main.h"
#endif
