package brotli

import "io"

/* Copyright 2015 Google Inc. All Rights Reserved.

   Distributed under MIT license.
   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/

/* Brotli state for partial streaming decoding. */
const (
	stateUninited = iota
	stateLargeWindowBits
	stateInitialize
	stateMetablockBegin
	stateMetablockHeader
	stateMetablockHeader2
	stateContextModes
	stateCommandBegin
	stateCommandInner
	stateCommandPostDecodeLiterals
	stateCommandPostWrapCopy
	stateUncompressed
	stateMetadata
	stateCommandInnerWrite
	stateMetablockDone
	stateCommandPostWrite1
	stateCommandPostWrite2
	stateHuffmanCode0
	stateHuffmanCode1
	stateHuffmanCode2
	stateHuffmanCode3
	stateContextMap1
	stateContextMap2
	stateTreeGroup
	stateDone
)

const (
	stateMetablockHeaderNone = iota
	stateMetablockHeaderEmpty
	stateMetablockHeaderNibbles
	stateMetablockHeaderSize
	stateMetablockHeaderUncompressed
	stateMetablockHeaderReserved
	stateMetablockHeaderBytes
	stateMetablockHeaderMetadata
)

const (
	stateUncompressedNone = iota
	stateUncompressedWrite
)

const (
	stateTreeGroupNone = iota
	stateTreeGroupLoop
)

const (
	stateContextMapNone = iota
	stateContextMapReadPrefix
	stateContextMapHuffman
	stateContextMapDecode
	stateContextMapTransform
)

const (
	stateHuffmanNone = iota
	stateHuffmanSimpleSize
	stateHuffmanSimpleRead
	stateHuffmanSimpleBuild
	stateHuffmanComplex
	stateHuffmanLengthSymbols
)

const (
	stateDecodeUint8None = iota
	stateDecodeUint8Short
	stateDecodeUint8Long
)

const (
	stateReadBlockLengthNone = iota
	stateReadBlockLengthSuffix
)

type Reader struct {
	src io.Reader
	buf []byte // scratch space for reading from src
	in  []byte // current chunk to decode; usually aliases buf

	state        int
	loop_counter int
	br           bitReader
	buffer       struct {
		u64 uint64
		u8  [8]byte
	}
	buffer_length               uint32
	pos                         int
	max_backward_distance       int
	max_distance                int
	ringbuffer_size             int
	ringbuffer_mask             int
	dist_rb_idx                 int
	dist_rb                     [4]int
	error_code                  int
	sub_loop_counter            uint32
	ringbuffer                  []byte
	ringbuffer_end              []byte
	htree_command               []huffmanCode
	context_lookup              []byte
	context_map_slice           []byte
	dist_context_map_slice      []byte
	literal_hgroup              huffmanTreeGroup
	insert_copy_hgroup          huffmanTreeGroup
	distance_hgroup             huffmanTreeGroup
	block_type_trees            []huffmanCode
	block_len_trees             []huffmanCode
	trivial_literal_context     int
	distance_context            int
	meta_block_remaining_len    int
	block_length_index          uint32
	block_length                [3]uint32
	num_block_types             [3]uint32
	block_type_rb               [6]uint32
	distance_postfix_bits       uint32
	num_direct_distance_codes   uint32
	distance_postfix_mask       int
	num_dist_htrees             uint32
	dist_context_map            []byte
	literal_htree               []huffmanCode
	dist_htree_index            byte
	repeat_code_len             uint32
	prev_code_len               uint32
	copy_length                 int
	distance_code               int
	rb_roundtrips               uint
	partial_pos_out             uint
	symbol                      uint32
	repeat                      uint32
	space                       uint32
	table                       [32]huffmanCode
	symbol_lists                symbolList
	symbols_lists_array         [huffmanMaxCodeLength + 1 + numCommandSymbols]uint16
	next_symbol                 [32]int
	code_length_code_lengths    [codeLengthCodes]byte
	code_length_histo           [16]uint16
	htree_index                 int
	next                        []huffmanCode
	context_index               uint32
	max_run_length_prefix       uint32
	code                        uint32
	context_map_table           [huffmanMaxSize272]huffmanCode
	substate_metablock_header   int
	substate_tree_group         int
	substate_context_map        int
	substate_uncompressed       int
	substate_huffman            int
	substate_decode_uint8       int
	substate_read_block_length  int
	is_last_metablock           uint
	is_uncompressed             uint
	is_metadata                 uint
	should_wrap_ringbuffer      uint
	canny_ringbuffer_allocation uint
	large_window                bool
	size_nibbles                uint
	window_bits                 uint32
	new_ringbuffer_size         int
	num_literal_htrees          uint32
	context_map                 []byte
	context_modes               []byte
	dictionary                  *dictionary
	transforms                  *transforms
	trivial_literal_contexts    [8]uint32
}

func decoderStateInit(s *Reader) bool {
	s.error_code = 0 /* BROTLI_DECODER_NO_ERROR */

	initBitReader(&s.br)
	s.state = stateUninited
	s.large_window = false
	s.substate_metablock_header = stateMetablockHeaderNone
	s.substate_tree_group = stateTreeGroupNone
	s.substate_context_map = stateContextMapNone
	s.substate_uncompressed = stateUncompressedNone
	s.substate_huffman = stateHuffmanNone
	s.substate_decode_uint8 = stateDecodeUint8None
	s.substate_read_block_length = stateReadBlockLengthNone

	s.buffer_length = 0
	s.loop_counter = 0
	s.pos = 0
	s.rb_roundtrips = 0
	s.partial_pos_out = 0

	clear(s.block_type_trees)
	s.block_len_trees = nil
	s.ringbuffer_size = 0
	s.new_ringbuffer_size = 0
	s.ringbuffer_mask = 0

	s.context_map = nil
	s.context_modes = nil
	s.dist_context_map = nil
	s.context_map_slice = nil
	s.dist_context_map_slice = nil

	s.sub_loop_counter = 0

	cleanupCodes(s)
	cleanupHTrees(s)

	s.is_last_metablock = 0
	s.is_uncompressed = 0
	s.is_metadata = 0
	s.should_wrap_ringbuffer = 0
	s.canny_ringbuffer_allocation = 1

	s.window_bits = 0
	s.max_distance = 0
	s.dist_rb[0] = 16
	s.dist_rb[1] = 15
	s.dist_rb[2] = 11
	s.dist_rb[3] = 4
	s.dist_rb_idx = 0

	s.symbol_lists.storage = s.symbols_lists_array[:]
	s.symbol_lists.offset = huffmanMaxCodeLength + 1

	s.dictionary = getDictionary()
	s.transforms = getTransforms()

	return true
}

func decoderStateMetablockBegin(s *Reader) {
	s.meta_block_remaining_len = 0
	s.block_length[0] = 1 << 24
	s.block_length[1] = 1 << 24
	s.block_length[2] = 1 << 24
	s.num_block_types[0] = 1
	s.num_block_types[1] = 1
	s.num_block_types[2] = 1
	s.block_type_rb[0] = 1
	s.block_type_rb[1] = 0
	s.block_type_rb[2] = 1
	s.block_type_rb[3] = 0
	s.block_type_rb[4] = 1
	s.block_type_rb[5] = 0
	s.context_map = nil
	s.context_modes = nil
	s.dist_context_map = nil
	s.context_map_slice = nil
	s.literal_htree = nil
	s.dist_context_map_slice = nil
	s.dist_htree_index = 0
	s.context_lookup = nil

	cleanupCodes(s)
	cleanupHTrees(s)
}

func decoderStateCleanupAfterMetablock(s *Reader) {
	s.context_modes = nil
	s.context_map = nil
	s.dist_context_map = nil
	cleanupHTrees(s)
}

func decoderHuffmanTreeGroupInit(group *huffmanTreeGroup, alphabet_size uint32, max_symbol uint32, ntrees uint32) {
	var max_table_size uint = uint(kMaxHuffmanTableSize[(alphabet_size+31)>>5])
	group.alphabet_size = uint16(alphabet_size)
	group.max_symbol = uint16(max_symbol)
	group.num_htrees = uint16(ntrees)

	makeCodesBuffer(group, uint(ntrees)*max_table_size)
	makeTreesBuffer(group, ntrees)
}

func makeTreesBuffer(group *huffmanTreeGroup, treesLen uint32) {
	if cap(group.htrees) < int(treesLen) {
		group.htrees = make([][]huffmanCode, treesLen)
		return
	}

	group.htrees = group.htrees[:treesLen]
}

func makeCodesBuffer(group *huffmanTreeGroup, codesLen uint) {
	if cap(group.codes) < int(codesLen) {
		group.codes = make([]huffmanCode, codesLen)
		return
	}

	group.codes = group.codes[:codesLen]
}

func cleanupCodes(s *Reader) {
	clear(s.literal_hgroup.codes)
	clear(s.insert_copy_hgroup.codes)
	clear(s.distance_hgroup.codes)
}

func cleanupHTrees(s *Reader) {
	clear(s.literal_hgroup.htrees)
	clear(s.insert_copy_hgroup.htrees)
	clear(s.distance_hgroup.htrees)
}
