package org.jetbrains.debugger.sourcemap;

final class Base64VLQ {
  private Base64VLQ() {
  }

  interface CharIterator {
    boolean hasNext();
    char next();
  }

  // A Base64 VLQ digit can represent 5 bits, so it is base-32.
  private static final int VLQ_BASE_SHIFT = 5;
  private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT;

  // A mask of bits for a VLQ digit (11111), 31 decimal.
  private static final int VLQ_BASE_MASK = VLQ_BASE - 1;

  // The continuation bit is the 6th bit.
  private static final int VLQ_CONTINUATION_BIT = VLQ_BASE;

  /**
   * Decodes the next VLQValue from the provided CharIterator.
   */
  public static int decode(CharIterator in) {
    int result = 0;
    int shift = 0;
    int digit;
    do {
      digit = Base64.BASE64_DECODE_MAP[in.next()];
      assert (digit != -1) : "invalid char";

      result += (digit & VLQ_BASE_MASK) << shift;
      shift += VLQ_BASE_SHIFT;
    }
    while ((digit & VLQ_CONTINUATION_BIT) != 0);

    boolean negate = (result & 1) == 1;
    result >>= 1;
    return negate ? -result : result;
  }

  private static final class Base64 {
    /**
     * A map used to convert integer values in the range 0-63 to their base64
     * values.
     */
    private static final String BASE64_MAP =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
      "abcdefghijklmnopqrstuvwxyz" +
      "0123456789+/";

    /**
     * A map used to convert base64 character into integer values.
     */
    private static final int[] BASE64_DECODE_MAP = new int[256];

    static {
      for (int i = 0; i < BASE64_MAP.length(); i++) {
        BASE64_DECODE_MAP[BASE64_MAP.charAt(i)] = i;
      }
    }
  }
}