/**
 * DataInputStream for decoding fast-compact encoded number sequences
 *
 * @author ab
 */
package btools.util;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;


public final class MixCoderDataInputStream extends DataInputStream {
  private int lastValue;
  private int repCount;
  private int diffshift;

  private int bits; // bits left in buffer
  private int b; // buffer word

  private static final int[] vl_values = BitCoderContext.vl_values;
  private static final int[] vl_length = BitCoderContext.vl_length;

  public MixCoderDataInputStream(InputStream is) {
    super(is);
  }

  public int readMixed() throws IOException {
    if (repCount == 0) {
      boolean negative = decodeBit();
      int d = decodeVarBits() + diffshift;
      repCount = decodeVarBits() + 1;
      lastValue += negative ? -d : d;
      diffshift = 1;
    }
    repCount--;
    return lastValue;
  }

  public boolean decodeBit() throws IOException {
    fillBuffer();
    boolean value = ((b & 1) != 0);
    b >>>= 1;
    bits--;
    return value;
  }

  public int decodeVarBits2() throws IOException {
    int range = 0;
    while (!decodeBit()) {
      range = 2 * range + 1;
    }
    return range + decodeBounded(range);
  }

  /**
   * decode an integer in the range 0..max (inclusive).
   *
   * @see #encodeBounded
   */
  public int decodeBounded(int max) throws IOException {
    int value = 0;
    int im = 1; // integer mask
    while ((value | im) <= max) {
      if (decodeBit()) {
        value |= im;
      }
      im <<= 1;
    }
    return value;
  }


  /**
   * @see #encodeVarBits
   */

  public int decodeVarBits() throws IOException {
    fillBuffer();
    int b12 = b & 0xfff;
    int len = vl_length[b12];
    if (len <= 12) {
      b >>>= len;
      bits -= len;
      return vl_values[b12]; // full value lookup
    }
    if (len <= 23) // // only length lookup
    {
      int len2 = len >> 1;
      b >>>= (len2 + 1);
      int mask = 0xffffffff >>> (32 - len2);
      mask += b & mask;
      b >>>= len2;
      bits -= len;
      return mask;
    }
    if ((b & 0xffffff) != 0) {
      // here we just know len in [25..47]
      // ( fillBuffer guarantees only 24 bits! )
      b >>>= 12;
      int len3 = 1 + (vl_length[b & 0xfff] >> 1);
      b >>>= len3;
      int len2 = 11 + len3;
      bits -= len2 + 1;
      fillBuffer();
      int mask = 0xffffffff >>> (32 - len2);
      mask += b & mask;
      b >>>= len2;
      bits -= len2;
      return mask;
    }
    return decodeVarBits2(); // no chance, use the slow one
  }

  private void fillBuffer() throws IOException {
    while (bits < 24) {
      int nextByte = read();

      if (nextByte != -1) {
        b |= (nextByte & 0xff) << bits;
      }
      bits += 8;
    }
  }

}
