///|
priv struct BrotliBlockSwitchHeader {
  num_types : Int
  type_tree : FixedArray[Int]?
  length_tree : FixedArray[Int]?
  first_length : Int
}

///|
priv struct BrotliCompressedMetablockHeader {
  literal_block : BrotliBlockSwitchHeader
  command_block : BrotliBlockSwitchHeader
  distance_block : BrotliBlockSwitchHeader
  distance_postfix_bits : Int
  num_direct_distance_codes : Int
  context_modes : FixedArray[Byte]
  literal_context_map : BrotliContextMap
  distance_context_map : BrotliContextMap
  literal_trees : BrotliHuffmanTreeGroup
  command_trees : BrotliHuffmanTreeGroup
  distance_trees : BrotliHuffmanTreeGroup
}

///|
fn brotli_read_block_switch_header(
  reader : BrotliBitReader,
) -> BrotliBlockSwitchHeader raise @common.FbrError {
  let num_types = brotli_read_block_type_count(reader)
  if num_types == 1 {
    return {
      num_types,
      type_tree: None,
      length_tree: None,
      first_length: @common.brotli_block_size_cap,
    }
  }
  let type_tree = brotli_read_huffman_code(
    reader,
    num_types + 2,
    num_types + 2,
    @common.brotli_huffman_table_bits,
  )
  let length_tree = brotli_read_huffman_code(
    reader, @common.brotli_num_block_length_symbols, @common.brotli_num_block_length_symbols,
    @common.brotli_huffman_table_bits,
  )
  {
    num_types,
    type_tree: Some(type_tree),
    length_tree: Some(length_tree),
    first_length: brotli_read_block_length(reader, length_tree),
  }
}

///|
fn brotli_read_context_modes(
  reader : BrotliBitReader,
  num_literal_block_types : Int,
) -> FixedArray[Byte] raise @common.FbrError {
  let modes = FixedArray::make(num_literal_block_types, b'\x00')
  for i in 0.. BrotliCompressedMetablockHeader raise @common.FbrError {
  let literal_block = brotli_read_block_switch_header(reader)
  let command_block = brotli_read_block_switch_header(reader)
  let distance_block = brotli_read_block_switch_header(reader)
  let bits = reader.take_bits(6).reinterpret_as_int()
  let distance_postfix_bits = bits & 3
  let num_direct_distance_codes = bits >> 2 << distance_postfix_bits
  let context_modes = brotli_read_context_modes(reader, literal_block.num_types)
  let literal_context_map = brotli_read_context_map(
    reader,
    literal_block.num_types << @common.brotli_literal_context_bits,
  )
  let distance_context_map = brotli_read_context_map(
    reader,
    distance_block.num_types << @common.brotli_distance_context_bits,
  )
  let distance_alphabet_size = @common.brotli_distance_alphabet_size(
    distance_postfix_bits, num_direct_distance_codes, @common.brotli_max_distance_bits,
  )
  let literal_trees = brotli_read_huffman_tree_group(
    reader,
    @common.brotli_num_literal_symbols,
    @common.brotli_num_literal_symbols,
    literal_context_map.num_trees,
    @common.brotli_huffman_table_bits,
  )
  let command_trees = brotli_read_huffman_tree_group(
    reader,
    @common.brotli_num_command_symbols,
    @common.brotli_num_command_symbols,
    command_block.num_types,
    @common.brotli_huffman_table_bits,
  )
  let distance_trees = brotli_read_huffman_tree_group(
    reader,
    distance_alphabet_size,
    distance_alphabet_size,
    distance_context_map.num_trees,
    @common.brotli_huffman_table_bits,
  )
  {
    literal_block,
    command_block,
    distance_block,
    distance_postfix_bits,
    num_direct_distance_codes,
    context_modes,
    literal_context_map,
    distance_context_map,
    literal_trees,
    command_trees,
    distance_trees,
  }
}