import 'dart:typed_data';

import 'bzip2/bz2_bit_reader.dart';
import 'bzip2/bzip2.dart';
import 'util/archive_exception.dart';
import 'util/byte_order.dart';
import 'util/input_stream.dart';
import 'util/output_stream.dart';

/// Decompress bzip2 compressed data.
/// Derived from libbzip2 (http://www.bzip.org).
class BZip2Decoder {
  List<int> decodeBytes(List<int> data, {bool verify = false}) {
    return decodeBuffer(InputStream(data, byteOrder: BIG_ENDIAN),
        verify: verify);
  }

  void decodeStream(InputStreamBase input, OutputStreamBase output,
      {bool verify = false}) {
    final br = Bz2BitReader(input);

    _groupPos = 0;
    _groupNo = 0;
    _gSel = 0;
    _gMinlen = 0;

    if (br.readByte() != BZip2.bzhSignature[0] ||
        br.readByte() != BZip2.bzhSignature[1] ||
        br.readByte() != BZip2.bzhSignature[2]) {
      throw ArchiveException('Invalid Signature');
    }

    _blockSize100k = br.readByte() - BZip2.hdr0;
    if (_blockSize100k < 0 || _blockSize100k > 9) {
      throw ArchiveException('Invalid BlockSize');
    }

    _tt = Uint32List(_blockSize100k * 100000);

    var combinedCrc = 0;

    while (true) {
      final type = _readBlockType(br);
      if (type == blockCompressed) {
        var storedBlockCrc = 0;
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();

        var blockCrc = _readCompressed(br, output);
        blockCrc = BZip2.finalizeCrc(blockCrc);

        if (verify && blockCrc != storedBlockCrc) {
          throw ArchiveException('Invalid block checksum.');
        }
        combinedCrc = ((combinedCrc << 1) | (combinedCrc >> 31)) & 0xffffffff;
        combinedCrc ^= blockCrc;
      } else if (type == blockEos) {
        var storedCrc = 0;
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();

        if (verify && storedCrc != combinedCrc) {
          throw ArchiveException(
              'Invalid combined checksum: $combinedCrc : $storedCrc');
        }

        output.flush();
        return;
      }
    }
  }

  List<int> decodeBuffer(InputStreamBase input,
      {bool verify = false, OutputStreamBase? output}) {
    output ??= OutputStream();
    final br = Bz2BitReader(input);

    _groupPos = 0;
    _groupNo = 0;
    _gSel = 0;
    _gMinlen = 0;

    if (br.readByte() != BZip2.bzhSignature[0] ||
        br.readByte() != BZip2.bzhSignature[1] ||
        br.readByte() != BZip2.bzhSignature[2]) {
      throw ArchiveException('Invalid Signature');
    }

    _blockSize100k = br.readByte() - BZip2.hdr0;
    if (_blockSize100k < 0 || _blockSize100k > 9) {
      throw ArchiveException('Invalid BlockSize');
    }

    _tt = Uint32List(_blockSize100k * 100000);

    var combinedCrc = 0;

    while (true) {
      final type = _readBlockType(br);
      if (type == blockCompressed) {
        var storedBlockCrc = 0;
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();
        storedBlockCrc = (storedBlockCrc << 8) | br.readByte();

        var blockCrc = _readCompressed(br, output);
        blockCrc = BZip2.finalizeCrc(blockCrc);

        if (verify && blockCrc != storedBlockCrc) {
          throw ArchiveException('Invalid block checksum.');
        }
        combinedCrc = ((combinedCrc << 1) | (combinedCrc >> 31)) & 0xffffffff;
        combinedCrc ^= blockCrc;
      } else if (type == blockEos) {
        var storedCrc = 0;
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();
        storedCrc = (storedCrc << 8) | br.readByte();

        if (verify && storedCrc != combinedCrc) {
          throw ArchiveException(
              'Invalid combined checksum: $combinedCrc : $storedCrc');
        }

        output.flush();

        if (output is! OutputStream) {
          return [];
        }

        return output.getBytes();
      }
    }
  }

  int _readBlockType(Bz2BitReader br) {
    var eos = true;
    var compressed = true;

    // .eos_magic:48        0x177245385090 (BCD sqrt(pi))
    // .compressed_magic:48 0x314159265359 (BCD (pi))
    for (var i = 0; i < 6; ++i) {
      var b = br.readByte();
      if (b != BZip2.compressedMagic[i]) {
        compressed = false;
      }
      if (b != BZip2.eosMagic[i]) {
        eos = false;
      }
      if (!eos && !compressed) {
        throw ArchiveException('Invalid Block Signature');
      }
    }

    return (compressed) ? blockCompressed : blockEos;
  }

  int _readCompressed(Bz2BitReader br, OutputStreamBase output) {
    var blockRandomized = br.readBits(1);
    var origPtr = br.readBits(8);
    origPtr = (origPtr << 8) | br.readBits(8);
    origPtr = (origPtr << 8) | br.readBits(8);

    // Receive the mapping table
    _inUse16 = Uint8List(16);
    for (var i = 0; i < 16; ++i) {
      _inUse16[i] = br.readBits(1);
    }

    _inUse = Uint8List(256);
    for (var i = 0, k = 0; i < 16; ++i, k += 16) {
      if (_inUse16[i] != 0) {
        for (var j = 0; j < 16; ++j) {
          _inUse[k + j] = br.readBits(1);
        }
      }
    }

    _makeMaps();
    if (_numInUse == 0) {
      throw ArchiveException('Data error');
    }

    var alphaSize = _numInUse + 2;

    // Now the selectors
    var numGroups = br.readBits(3);
    if (numGroups < 2 || numGroups > 6) {
      throw ArchiveException('Data error');
    }

    _numSelectors = br.readBits(15);
    if (_numSelectors < 1) {
      throw ArchiveException('Data error');
    }

    _selectorMtf = Uint8List(bzMaxSelectors);
    _selector = Uint8List(bzMaxSelectors);

    for (var i = 0; i < _numSelectors; ++i) {
      var j = 0;
      while (true) {
        var b = br.readBits(1);
        if (b == 0) {
          break;
        }
        j++;
        if (j >= numGroups) {
          throw ArchiveException('Data error');
        }
      }

      _selectorMtf[i] = j;
    }

    // Undo the MTF values for the selectors.
    final pos = Uint8List(bzNGroups);
    for (var i = 0; i < numGroups; ++i) {
      pos[i] = i;
    }

    for (var i = 0; i < _numSelectors; ++i) {
      var v = _selectorMtf[i];
      var tmp = pos[v];
      while (v > 0) {
        pos[v] = pos[v - 1];
        v--;
      }
      pos[0] = tmp;
      _selector[i] = tmp;
    }

    // Now the coding tables
    _len = List<Uint8List>.filled(bzNGroups, BZip2.emptyUint8List);

    for (var t = 0; t < numGroups; ++t) {
      _len[t] = Uint8List(bzMaxAlphaSize);

      var c = br.readBits(5);
      for (var i = 0; i < alphaSize; ++i) {
        while (true) {
          if (c < 1 || c > 20) {
            throw ArchiveException('Data error');
          }
          var b = br.readBits(1);
          if (b == 0) {
            break;
          }
          b = br.readBits(1);
          if (b == 0) {
            c++;
          } else {
            c--;
          }
        }
        _len[t][i] = c;
      }
    }

    // Create the Huffman decoding tables
    _limit = List<Int32List>.filled(bzNGroups, BZip2.emptyInt32List);
    _base = List<Int32List>.filled(bzNGroups, BZip2.emptyInt32List);
    _perm = List<Int32List>.filled(bzNGroups, BZip2.emptyInt32List);
    _minLens = Int32List(bzNGroups);

    for (var t = 0; t < numGroups; t++) {
      _limit[t] = Int32List(bzMaxAlphaSize);
      _base[t] = Int32List(bzMaxAlphaSize);
      _perm[t] = Int32List(bzMaxAlphaSize);

      var minLen = 32;
      var maxLen = 0;
      for (var i = 0; i < alphaSize; ++i) {
        if (_len[t][i] > maxLen) {
          maxLen = _len[t][i];
        }
        if (_len[t][i] < minLen) {
          minLen = _len[t][i];
        }
      }

      _hbCreateDecodeTables(
          _limit[t], _base[t], _perm[t], _len[t], minLen, maxLen, alphaSize);

      _minLens[t] = minLen;
    }

    // Now the MTF values
    var eob = _numInUse + 1;
    var nblockMAX = 100000 * _blockSize100k;

    _unzftab = Int32List(256);

    // MTF init
    _mtfa = Uint8List(mtfaSize);
    _mtfbase = Int32List(256 ~/ mtflSize);

    var kk = mtfaSize - 1;
    for (var ii = 256 ~/ mtflSize - 1; ii >= 0; ii--) {
      for (var jj = mtflSize - 1; jj >= 0; jj--) {
        _mtfa[kk] = ii * mtflSize + jj;
        kk--;
      }
      _mtfbase[ii] = kk + 1;
    }

    var nblock = 0;
    _groupPos = 0;
    _groupNo = -1;
    var nextSym = _getMtfVal(br);
    var uc = 0;

    while (true) {
      if (nextSym == eob) {
        break;
      }

      if (nextSym == bzRunA || nextSym == bzRunB) {
        var es = -1;
        var N = 1;
        do {
          // Check that N doesn't get too big, so that es doesn't
          // go negative.  The maximum value that can be
          // RUNA/RUNB encoded is equal to the block size (post
          // the initial RLE), viz, 900k, so bounding N at 2
          // million should guard against overflow without
          // rejecting any legitimate inputs.
          if (N >= 2 * 1024 * 1024) {
            throw ArchiveException('Data error');
          }

          if (nextSym == bzRunA) {
            es = es + (0 + 1) * N;
          } else if (nextSym == bzRunB) {
            es = es + (1 + 1) * N;
          }

          N = N * 2;

          nextSym = _getMtfVal(br);
        } while (nextSym == bzRunA || nextSym == bzRunB);

        es++;

        uc = _seqToUnseq[_mtfa[_mtfbase[0]]];
        _unzftab[uc] += es;

        while (es > 0) {
          if (nblock >= nblockMAX) {
            throw ArchiveException('Data error');
          }

          _tt[nblock] = uc;

          nblock++;
          es--;
        }
        continue;
      } else {
        if (nblock >= nblockMAX) {
          throw ArchiveException('Data error');
        }

        // uc = MTF ( nextSym-1 )
        var nn = nextSym - 1;

        if (nn < mtflSize) {
          // avoid general-case expense
          var pp = _mtfbase[0];
          uc = _mtfa[pp + nn];
          while (nn > 3) {
            var z = pp + nn;
            _mtfa[(z)] = _mtfa[(z) - 1];
            _mtfa[(z) - 1] = _mtfa[(z) - 2];
            _mtfa[(z) - 2] = _mtfa[(z) - 3];
            _mtfa[(z) - 3] = _mtfa[(z) - 4];
            nn -= 4;
          }
          while (nn > 0) {
            _mtfa[(pp + nn)] = _mtfa[(pp + nn) - 1];
            nn--;
          }
          _mtfa[pp] = uc;
        } else {
          // general case
          var lno = nn ~/ mtflSize;
          var off = nn % mtflSize;
          var pp = _mtfbase[lno] + off;
          uc = _mtfa[pp];
          while (pp > _mtfbase[lno]) {
            _mtfa[pp] = _mtfa[pp - 1];
            pp--;
          }
          _mtfbase[lno]++;
          while (lno > 0) {
            _mtfbase[lno]--;
            _mtfa[_mtfbase[lno]] = _mtfa[_mtfbase[lno - 1] + mtflSize - 1];
            lno--;
          }
          _mtfbase[0]--;
          _mtfa[_mtfbase[0]] = uc;
          if (_mtfbase[0] == 0) {
            kk = mtfaSize - 1;
            for (var ii = 256 ~/ mtflSize - 1; ii >= 0; ii--) {
              for (var jj = mtflSize - 1; jj >= 0; jj--) {
                _mtfa[kk] = _mtfa[_mtfbase[ii] + jj];
                kk--;
              }
              _mtfbase[ii] = kk + 1;
            }
          }
        }

        // end uc = MTF ( nextSym-1 )
        _unzftab[_seqToUnseq[uc]]++;
        _tt[nblock] = (_seqToUnseq[uc]);
        nblock++;

        nextSym = _getMtfVal(br);
        continue;
      }
    }

    // Now we know what nblock is, we can do a better sanity
    // check on s->origPtr.
    if (origPtr < 0 || origPtr >= nblock) {
      throw ArchiveException('Data error');
    }

    // Set up cftab to facilitate generation of T^(-1)
    // Check: unzftab entries in range.
    for (var i = 0; i <= 255; i++) {
      if (_unzftab[i] < 0 || _unzftab[i] > nblock) {
        throw ArchiveException('Data error');
      }
    }

    // Actually generate cftab.
    _cftab = Int32List(257);
    _cftab[0] = 0;
    for (var i = 1; i <= 256; i++) {
      _cftab[i] = _unzftab[i - 1];
    }

    for (var i = 1; i <= 256; i++) {
      _cftab[i] += _cftab[i - 1];
    }

    // Check: cftab entries in range.
    for (var i = 0; i <= 256; i++) {
      if (_cftab[i] < 0 || _cftab[i] > nblock) {
        // s->cftab[i] can legitimately be == nblock
        throw ArchiveException('Data error');
      }
    }

    // Check: cftab entries non-descending.
    for (var i = 1; i <= 256; i++) {
      if (_cftab[i - 1] > _cftab[i]) {
        throw ArchiveException('Data error');
      }
    }

    // compute the T^(-1) vector
    for (var i = 0; i < nblock; i++) {
      uc = (_tt[i] & 0xff);
      _tt[_cftab[uc]] |= (i << 8);
      _cftab[uc]++;
    }

    var blockCrc = BZip2.initialCrc;

    var tPos = _tt[origPtr] >> 8;
    var numBlockUsed = 0;
    int k0;
    var rNToGo = 0;
    var rTPos = 0;

    if (blockRandomized != 0) {
      rNToGo = 0;
      rTPos = 0;

      if (tPos >= 100000 * _blockSize100k) {
        throw ArchiveException('Data error');
      }
      tPos = _tt[tPos];
      k0 = tPos & 0xff;
      tPos >>= 8;

      numBlockUsed++;

      if (rNToGo == 0) {
        rNToGo = bz2RNums[rTPos];
        rTPos++;
        if (rTPos == 512) {
          rTPos = 0;
        }
      }
      rNToGo--;

      k0 ^= ((rNToGo == 1) ? 1 : 0);
    } else {
      // c_tPos is unsigned, hence test < 0 is pointless.
      if (tPos >= 100000 * _blockSize100k) {
        return blockCrc;
      }
      tPos = _tt[tPos];
      k0 = (tPos & 0xff);
      tPos >>= 8;
      numBlockUsed++;
    }

    // UnRLE to output
    var cStateOutLen = 0;
    var cStateOutCh = 0;
    var sSaveNBlockPP = nblock + 1;
    var cNBlockUsed = numBlockUsed;
    var cK0 = k0;
    int k1;

    if (blockRandomized != 0) {
      while (true) {
        // try to finish existing run
        while (true) {
          if (cStateOutLen == 0) {
            break;
          }

          output.writeByte(cStateOutCh);
          blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc);

          cStateOutLen--;
        }

        // can a run be started?
        if (cNBlockUsed == sSaveNBlockPP) {
          return blockCrc;
        }

        // Only caused by corrupt data stream?
        if (cNBlockUsed > sSaveNBlockPP) {
          throw ArchiveException('Data error.');
        }

        cStateOutLen = 1;
        cStateOutCh = k0;
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        if (rNToGo == 0) {
          rNToGo = bz2RNums[rTPos];
          rTPos++;
          if (rTPos == 512) {
            rTPos = 0;
          }
        }
        rNToGo--;
        k1 ^= ((rNToGo == 1) ? 1 : 0);
        cNBlockUsed++;
        if (cNBlockUsed == sSaveNBlockPP) {
          continue;
        }
        if (k1 != k0) {
          k0 = k1;
          continue;
        }

        cStateOutLen = 2;
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        if (rNToGo == 0) {
          rNToGo = bz2RNums[rTPos];
          rTPos++;
          if (rTPos == 512) {
            rTPos = 0;
          }
        }
        k1 ^= ((rNToGo == 1) ? 1 : 0);
        cNBlockUsed++;
        if (cNBlockUsed == sSaveNBlockPP) {
          continue;
        }
        if (k1 != k0) {
          k0 = k1;
          continue;
        }

        cStateOutLen = 3;
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        if (rNToGo == 0) {
          rNToGo = bz2RNums[rTPos];
          rTPos++;
          if (rTPos == 512) {
            rTPos = 0;
          }
        }
        k1 ^= ((rNToGo == 1) ? 1 : 0);
        cNBlockUsed++;
        if (cNBlockUsed == sSaveNBlockPP) {
          continue;
        }
        if (k1 != k0) {
          k0 = k1;
          continue;
        }

        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        if (rNToGo == 0) {
          rNToGo = bz2RNums[rTPos];
          rTPos++;
          if (rTPos == 512) {
            rTPos = 0;
          }
        }
        k1 ^= ((rNToGo == 1) ? 1 : 0);
        cNBlockUsed++;
        cStateOutLen = k1 + 4;

        tPos = _tt[tPos];
        k0 = tPos & 0xff;
        tPos >>= 8;
        if (rNToGo == 0) {
          rNToGo = bz2RNums[rTPos];
          rTPos++;
          if (rTPos == 512) {
            rTPos = 0;
          }
        }
        k0 ^= ((rNToGo == 1) ? 1 : 0);
        cNBlockUsed++;
      }
    } else {
      while (true) {
        // try to finish existing run
        if (cStateOutLen > 0) {
          while (true) {
            if (cStateOutLen == 1) {
              break;
            }

            output.writeByte(cStateOutCh);
            blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc);

            cStateOutLen--;
          }

          output.writeByte(cStateOutCh);
          blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc);
        }

        // Only caused by corrupt data stream?
        if (cNBlockUsed > sSaveNBlockPP) {
          throw ArchiveException('Data error');
        }

        // can a run be started?
        if (cNBlockUsed == sSaveNBlockPP) {
          cStateOutLen = 0;
          return blockCrc;
        }

        cStateOutCh = cK0;

        int k1;

        if (tPos >= 100000 * _blockSize100k) {
          throw ArchiveException('Data Error');
        }
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;

        cNBlockUsed++;
        if (k1 != cK0) {
          cK0 = k1;
          output.writeByte(cStateOutCh);
          blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc);
          cStateOutLen = 0;
          continue;
        }

        if (cNBlockUsed == sSaveNBlockPP) {
          output.writeByte(cStateOutCh);
          blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc);
          cStateOutLen = 0;
          continue;
        }

        cStateOutLen = 2;
        if (tPos >= 100000 * _blockSize100k) {
          throw ArchiveException('Data Error');
        }
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        cNBlockUsed++;

        if (cNBlockUsed == sSaveNBlockPP) {
          continue;
        }

        if (k1 != cK0) {
          cK0 = k1;
          continue;
        }

        cStateOutLen = 3;
        if (tPos >= 100000 * _blockSize100k) {
          throw ArchiveException('Data Error');
        }
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        cNBlockUsed++;

        if (cNBlockUsed == sSaveNBlockPP) {
          continue;
        }

        if (k1 != cK0) {
          cK0 = k1;
          continue;
        }

        if (tPos >= 100000 * _blockSize100k) {
          throw ArchiveException('Data Error');
        }
        tPos = _tt[tPos];
        k1 = tPos & 0xff;
        tPos >>= 8;
        cNBlockUsed++;

        cStateOutLen = k1 + 4;

        if (tPos >= 100000 * _blockSize100k) {
          throw ArchiveException('Data Error');
        }
        tPos = _tt[tPos];
        cK0 = tPos & 0xff;
        tPos >>= 8;

        cNBlockUsed++;
      }
    }

    return blockCrc; // ignore: dead_code
  }

  int _getMtfVal(Bz2BitReader br) {
    if (_groupPos == 0) {
      _groupNo++;
      if (_groupNo >= _numSelectors) {
        throw ArchiveException('Data error');
      }

      _groupPos = bzGSize;
      _gSel = _selector[_groupNo];
      _gMinlen = _minLens[_gSel];
      _gLimit = _limit[_gSel];
      _gPerm = _perm[_gSel];
      _gBase = _base[_gSel];
    }

    _groupPos--;
    var zn = _gMinlen;
    var zvec = br.readBits(zn);

    while (true) {
      if (zn > 20) {
        throw ArchiveException('Data error');
      }
      if (zvec <= _gLimit[zn]) {
        break;
      }

      zn++;
      var zj = br.readBits(1);
      zvec = (zvec << 1) | zj;
    }

    if (zvec - _gBase[zn] < 0 || zvec - _gBase[zn] >= bzMaxAlphaSize) {
      throw ArchiveException('Data error');
    }

    return _gPerm[zvec - _gBase[zn]];
  }

  void _hbCreateDecodeTables(Int32List limit, Int32List base, Int32List perm,
      Uint8List length, int minLen, int maxLen, int alphaSize) {
    var pp = 0;
    for (var i = minLen; i <= maxLen; i++) {
      for (var j = 0; j < alphaSize; j++) {
        if (length[j] == i) {
          perm[pp] = j;
          pp++;
        }
      }
    }

    for (var i = 0; i < bzMaxCodeLen; i++) {
      base[i] = 0;
    }

    for (var i = 0; i < alphaSize; i++) {
      base[length[i] + 1]++;
    }

    for (var i = 1; i < bzMaxCodeLen; i++) {
      base[i] += base[i - 1];
    }

    for (var i = 0; i < bzMaxCodeLen; i++) {
      limit[i] = 0;
    }

    var vec = 0;

    for (var i = minLen; i <= maxLen; i++) {
      vec += (base[i + 1] - base[i]);
      limit[i] = vec - 1;
      vec <<= 1;
    }

    for (var i = minLen + 1; i <= maxLen; i++) {
      base[i] = ((limit[i - 1] + 1) << 1) - base[i];
    }
  }

  void _makeMaps() {
    _numInUse = 0;
    _seqToUnseq = Uint8List(256);
    for (var i = 0; i < 256; ++i) {
      if (_inUse[i] != 0) {
        _seqToUnseq[_numInUse++] = i;
      }
    }
  }

  late int _blockSize100k;
  late Uint32List _tt;
  late Uint8List _inUse16;
  late Uint8List _inUse;
  late Uint8List _seqToUnseq;
  late Uint8List _mtfa;
  late Int32List _mtfbase;
  late Uint8List _selectorMtf;
  late Uint8List _selector;
  late List<Int32List> _limit;
  late List<Int32List> _base;
  late List<Int32List> _perm;
  late Int32List _minLens;
  late Int32List _unzftab;

  late int _numSelectors;
  int _groupPos = 0;
  int _groupNo = -1;
  int _gSel = 0;
  int _gMinlen = 0;
  late Int32List _gLimit;
  late Int32List _gPerm;
  late Int32List _gBase;
  late Int32List _cftab;

  late List<Uint8List> _len;
  int _numInUse = 0;

  static const int bzNGroups = 6;
  static const int bzGSize = 50;
  static const int bzNIters = 4;
  static const int bzMaxAlphaSize = 258;
  static const int bzMaxCodeLen = 23;
  static const int bzMaxSelectors = (2 + (900000 ~/ bzGSize));
  static const int mtfaSize = 4096;
  static const int mtflSize = 16;
  static const int bzRunA = 0;
  static const int bzRunB = 1;

  static const int blockCompressed = 0;
  static const int blockEos = 2;

  static const List<int> bz2RNums = [
    619,
    720,
    127,
    481,
    931,
    816,
    813,
    233,
    566,
    247,
    985,
    724,
    205,
    454,
    863,
    491,
    741,
    242,
    949,
    214,
    733,
    859,
    335,
    708,
    621,
    574,
    73,
    654,
    730,
    472,
    419,
    436,
    278,
    496,
    867,
    210,
    399,
    680,
    480,
    51,
    878,
    465,
    811,
    169,
    869,
    675,
    611,
    697,
    867,
    561,
    862,
    687,
    507,
    283,
    482,
    129,
    807,
    591,
    733,
    623,
    150,
    238,
    59,
    379,
    684,
    877,
    625,
    169,
    643,
    105,
    170,
    607,
    520,
    932,
    727,
    476,
    693,
    425,
    174,
    647,
    73,
    122,
    335,
    530,
    442,
    853,
    695,
    249,
    445,
    515,
    909,
    545,
    703,
    919,
    874,
    474,
    882,
    500,
    594,
    612,
    641,
    801,
    220,
    162,
    819,
    984,
    589,
    513,
    495,
    799,
    161,
    604,
    958,
    533,
    221,
    400,
    386,
    867,
    600,
    782,
    382,
    596,
    414,
    171,
    516,
    375,
    682,
    485,
    911,
    276,
    98,
    553,
    163,
    354,
    666,
    933,
    424,
    341,
    533,
    870,
    227,
    730,
    475,
    186,
    263,
    647,
    537,
    686,
    600,
    224,
    469,
    68,
    770,
    919,
    190,
    373,
    294,
    822,
    808,
    206,
    184,
    943,
    795,
    384,
    383,
    461,
    404,
    758,
    839,
    887,
    715,
    67,
    618,
    276,
    204,
    918,
    873,
    777,
    604,
    560,
    951,
    160,
    578,
    722,
    79,
    804,
    96,
    409,
    713,
    940,
    652,
    934,
    970,
    447,
    318,
    353,
    859,
    672,
    112,
    785,
    645,
    863,
    803,
    350,
    139,
    93,
    354,
    99,
    820,
    908,
    609,
    772,
    154,
    274,
    580,
    184,
    79,
    626,
    630,
    742,
    653,
    282,
    762,
    623,
    680,
    81,
    927,
    626,
    789,
    125,
    411,
    521,
    938,
    300,
    821,
    78,
    343,
    175,
    128,
    250,
    170,
    774,
    972,
    275,
    999,
    639,
    495,
    78,
    352,
    126,
    857,
    956,
    358,
    619,
    580,
    124,
    737,
    594,
    701,
    612,
    669,
    112,
    134,
    694,
    363,
    992,
    809,
    743,
    168,
    974,
    944,
    375,
    748,
    52,
    600,
    747,
    642,
    182,
    862,
    81,
    344,
    805,
    988,
    739,
    511,
    655,
    814,
    334,
    249,
    515,
    897,
    955,
    664,
    981,
    649,
    113,
    974,
    459,
    893,
    228,
    433,
    837,
    553,
    268,
    926,
    240,
    102,
    654,
    459,
    51,
    686,
    754,
    806,
    760,
    493,
    403,
    415,
    394,
    687,
    700,
    946,
    670,
    656,
    610,
    738,
    392,
    760,
    799,
    887,
    653,
    978,
    321,
    576,
    617,
    626,
    502,
    894,
    679,
    243,
    440,
    680,
    879,
    194,
    572,
    640,
    724,
    926,
    56,
    204,
    700,
    707,
    151,
    457,
    449,
    797,
    195,
    791,
    558,
    945,
    679,
    297,
    59,
    87,
    824,
    713,
    663,
    412,
    693,
    342,
    606,
    134,
    108,
    571,
    364,
    631,
    212,
    174,
    643,
    304,
    329,
    343,
    97,
    430,
    751,
    497,
    314,
    983,
    374,
    822,
    928,
    140,
    206,
    73,
    263,
    980,
    736,
    876,
    478,
    430,
    305,
    170,
    514,
    364,
    692,
    829,
    82,
    855,
    953,
    676,
    246,
    369,
    970,
    294,
    750,
    807,
    827,
    150,
    790,
    288,
    923,
    804,
    378,
    215,
    828,
    592,
    281,
    565,
    555,
    710,
    82,
    896,
    831,
    547,
    261,
    524,
    462,
    293,
    465,
    502,
    56,
    661,
    821,
    976,
    991,
    658,
    869,
    905,
    758,
    745,
    193,
    768,
    550,
    608,
    933,
    378,
    286,
    215,
    979,
    792,
    961,
    61,
    688,
    793,
    644,
    986,
    403,
    106,
    366,
    905,
    644,
    372,
    567,
    466,
    434,
    645,
    210,
    389,
    550,
    919,
    135,
    780,
    773,
    635,
    389,
    707,
    100,
    626,
    958,
    165,
    504,
    920,
    176,
    193,
    713,
    857,
    265,
    203,
    50,
    668,
    108,
    645,
    990,
    626,
    197,
    510,
    357,
    358,
    850,
    858,
    364,
    936,
    638
  ];
}
