/**
 * javascript port of java LZW decompression
 * Original java author url: https://gist.github.com/devunwired/4479231
 */

export const lzw = (minCodeSize, data, pixelCount) => {
  const MAX_STACK_SIZE = 4096
  const nullCode = -1
  const npix = pixelCount
  var available,
    clear,
    code_mask,
    code_size,
    end_of_information,
    in_code,
    old_code,
    bits,
    code,
    i,
    datum,
    data_size,
    first,
    top,
    bi,
    pi

  const dstPixels = new Array(pixelCount)
  const prefix = new Array(MAX_STACK_SIZE)
  const suffix = new Array(MAX_STACK_SIZE)
  const pixelStack = new Array(MAX_STACK_SIZE + 1)

  // Initialize GIF data stream decoder.
  data_size = minCodeSize
  clear = 1 << data_size
  end_of_information = clear + 1
  available = clear + 2
  old_code = nullCode
  code_size = data_size + 1
  code_mask = (1 << code_size) - 1
  for (code = 0; code < clear; code++) {
    prefix[code] = 0
    suffix[code] = code
  }

  // Decode GIF pixel stream.
  var datum, bits, count, first, top, pi, bi
  datum = bits = count = first = top = pi = bi = 0
  for (i = 0; i < npix; ) {
    if (top === 0) {
      if (bits < code_size) {
        // get the next byte
        datum += data[bi] << bits

        bits += 8
        bi++
        continue
      }
      // Get the next code.
      code = datum & code_mask
      datum >>= code_size
      bits -= code_size
      // Interpret the code
      if (code > available || code == end_of_information) {
        break
      }
      if (code == clear) {
        // Reset decoder.
        code_size = data_size + 1
        code_mask = (1 << code_size) - 1
        available = clear + 2
        old_code = nullCode
        continue
      }
      if (old_code == nullCode) {
        pixelStack[top++] = suffix[code]
        old_code = code
        first = code
        continue
      }
      in_code = code
      if (code == available) {
        pixelStack[top++] = first
        code = old_code
      }
      while (code > clear) {
        pixelStack[top++] = suffix[code]
        code = prefix[code]
      }

      first = suffix[code] & 0xff
      pixelStack[top++] = first

      // add a new string to the table, but only if space is available
      // if not, just continue with current table until a clear code is found
      // (deferred clear code implementation as per GIF spec)
      if (available < MAX_STACK_SIZE) {
        prefix[available] = old_code
        suffix[available] = first
        available++
        if ((available & code_mask) === 0 && available < MAX_STACK_SIZE) {
          code_size++
          code_mask += available
        }
      }
      old_code = in_code
    }
    // Pop a pixel off the pixel stack.
    top--
    dstPixels[pi++] = pixelStack[top]
    i++
  }

  for (i = pi; i < npix; i++) {
    dstPixels[i] = 0 // clear missing pixels
  }

  return dstPixels
}
