package brotli

/* Copyright 2013 Google Inc. All Rights Reserved.

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

/* Utilities for building Huffman decoding tables. */

const huffmanMaxCodeLength = 15

/* Maximum possible Huffman table size for an alphabet size of (index * 32),
   max code length 15 and root table bits 8. */
var kMaxHuffmanTableSize = []uint16{
	256,
	402,
	436,
	468,
	500,
	534,
	566,
	598,
	630,
	662,
	694,
	726,
	758,
	790,
	822,
	854,
	886,
	920,
	952,
	984,
	1016,
	1048,
	1080,
	1112,
	1144,
	1176,
	1208,
	1240,
	1272,
	1304,
	1336,
	1368,
	1400,
	1432,
	1464,
	1496,
	1528,
}

/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */
const huffmanMaxSize26 = 396

/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */
const huffmanMaxSize258 = 632

/* BROTLI_MAX_CONTEXT_MAP_SYMBOLS == 272 */
const huffmanMaxSize272 = 646

const huffmanMaxCodeLengthCodeLength = 5

/* Do not create this struct directly - use the ConstructHuffmanCode
 * constructor below! */
type huffmanCode struct {
	bits  byte
	value uint16
}

func constructHuffmanCode(bits byte, value uint16) huffmanCode {
	var h huffmanCode
	h.bits = bits
	h.value = value
	return h
}

/* Builds Huffman lookup table assuming code lengths are in symbol order. */

/* Builds Huffman lookup table assuming code lengths are in symbol order.
   Returns size of resulting table. */

/* Builds a simple Huffman table. The |num_symbols| parameter is to be
   interpreted as follows: 0 means 1 symbol, 1 means 2 symbols,
   2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2],
   4 means 4 symbols with lengths [1, 2, 3, 3]. */

/* Contains a collection of Huffman trees with the same alphabet size. */
/* max_symbol is needed due to simple codes since log2(alphabet_size) could be
   greater than log2(max_symbol). */
type huffmanTreeGroup struct {
	htrees        [][]huffmanCode
	codes         []huffmanCode
	alphabet_size uint16
	max_symbol    uint16
	num_htrees    uint16
}

const reverseBitsMax = 8

const reverseBitsBase = 0

var kReverseBits = [1 << reverseBitsMax]byte{
	0x00,
	0x80,
	0x40,
	0xC0,
	0x20,
	0xA0,
	0x60,
	0xE0,
	0x10,
	0x90,
	0x50,
	0xD0,
	0x30,
	0xB0,
	0x70,
	0xF0,
	0x08,
	0x88,
	0x48,
	0xC8,
	0x28,
	0xA8,
	0x68,
	0xE8,
	0x18,
	0x98,
	0x58,
	0xD8,
	0x38,
	0xB8,
	0x78,
	0xF8,
	0x04,
	0x84,
	0x44,
	0xC4,
	0x24,
	0xA4,
	0x64,
	0xE4,
	0x14,
	0x94,
	0x54,
	0xD4,
	0x34,
	0xB4,
	0x74,
	0xF4,
	0x0C,
	0x8C,
	0x4C,
	0xCC,
	0x2C,
	0xAC,
	0x6C,
	0xEC,
	0x1C,
	0x9C,
	0x5C,
	0xDC,
	0x3C,
	0xBC,
	0x7C,
	0xFC,
	0x02,
	0x82,
	0x42,
	0xC2,
	0x22,
	0xA2,
	0x62,
	0xE2,
	0x12,
	0x92,
	0x52,
	0xD2,
	0x32,
	0xB2,
	0x72,
	0xF2,
	0x0A,
	0x8A,
	0x4A,
	0xCA,
	0x2A,
	0xAA,
	0x6A,
	0xEA,
	0x1A,
	0x9A,
	0x5A,
	0xDA,
	0x3A,
	0xBA,
	0x7A,
	0xFA,
	0x06,
	0x86,
	0x46,
	0xC6,
	0x26,
	0xA6,
	0x66,
	0xE6,
	0x16,
	0x96,
	0x56,
	0xD6,
	0x36,
	0xB6,
	0x76,
	0xF6,
	0x0E,
	0x8E,
	0x4E,
	0xCE,
	0x2E,
	0xAE,
	0x6E,
	0xEE,
	0x1E,
	0x9E,
	0x5E,
	0xDE,
	0x3E,
	0xBE,
	0x7E,
	0xFE,
	0x01,
	0x81,
	0x41,
	0xC1,
	0x21,
	0xA1,
	0x61,
	0xE1,
	0x11,
	0x91,
	0x51,
	0xD1,
	0x31,
	0xB1,
	0x71,
	0xF1,
	0x09,
	0x89,
	0x49,
	0xC9,
	0x29,
	0xA9,
	0x69,
	0xE9,
	0x19,
	0x99,
	0x59,
	0xD9,
	0x39,
	0xB9,
	0x79,
	0xF9,
	0x05,
	0x85,
	0x45,
	0xC5,
	0x25,
	0xA5,
	0x65,
	0xE5,
	0x15,
	0x95,
	0x55,
	0xD5,
	0x35,
	0xB5,
	0x75,
	0xF5,
	0x0D,
	0x8D,
	0x4D,
	0xCD,
	0x2D,
	0xAD,
	0x6D,
	0xED,
	0x1D,
	0x9D,
	0x5D,
	0xDD,
	0x3D,
	0xBD,
	0x7D,
	0xFD,
	0x03,
	0x83,
	0x43,
	0xC3,
	0x23,
	0xA3,
	0x63,
	0xE3,
	0x13,
	0x93,
	0x53,
	0xD3,
	0x33,
	0xB3,
	0x73,
	0xF3,
	0x0B,
	0x8B,
	0x4B,
	0xCB,
	0x2B,
	0xAB,
	0x6B,
	0xEB,
	0x1B,
	0x9B,
	0x5B,
	0xDB,
	0x3B,
	0xBB,
	0x7B,
	0xFB,
	0x07,
	0x87,
	0x47,
	0xC7,
	0x27,
	0xA7,
	0x67,
	0xE7,
	0x17,
	0x97,
	0x57,
	0xD7,
	0x37,
	0xB7,
	0x77,
	0xF7,
	0x0F,
	0x8F,
	0x4F,
	0xCF,
	0x2F,
	0xAF,
	0x6F,
	0xEF,
	0x1F,
	0x9F,
	0x5F,
	0xDF,
	0x3F,
	0xBF,
	0x7F,
	0xFF,
}

const reverseBitsLowest = (uint64(1) << (reverseBitsMax - 1 + reverseBitsBase))

/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX),
   where reverse(value, len) is the bit-wise reversal of the len least
   significant bits of value. */
func reverseBits8(num uint64) uint64 {
	return uint64(kReverseBits[num])
}

/* Stores code in table[0], table[step], table[2*step], ..., table[end] */
/* Assumes that end is an integer multiple of step */
func replicateValue(table []huffmanCode, step int, end int, code huffmanCode) {
	for {
		end -= step
		table[end] = code
		if end <= 0 {
			break
		}
	}
}

/* Returns the table width of the next 2nd level table. |count| is the histogram
   of bit lengths for the remaining symbols, |len| is the code length of the
   next processed symbol. */
func nextTableBitSize(count []uint16, len int, root_bits int) int {
	var left int = 1 << uint(len-root_bits)
	for len < huffmanMaxCodeLength {
		left -= int(count[len])
		if left <= 0 {
			break
		}
		len++
		left <<= 1
	}

	return len - root_bits
}

func buildCodeLengthsHuffmanTable(table []huffmanCode, code_lengths []byte, count []uint16) {
	var code huffmanCode /* current table entry */ /* symbol index in original or sorted table */ /* prefix code */ /* prefix code addend */ /* step size to replicate values in current table */ /* size of current table */ /* symbols sorted by code length */
	var symbol int
	var key uint64
	var key_step uint64
	var step int
	var table_size int
	var sorted [codeLengthCodes]int
	var offset [huffmanMaxCodeLengthCodeLength + 1]int
	var bits int
	var bits_count int
	/* offsets in sorted table for each length */
	assert(huffmanMaxCodeLengthCodeLength <= reverseBitsMax)

	/* Generate offsets into sorted symbol table by code length. */
	symbol = -1

	bits = 1
	var i int
	for i = 0; i < huffmanMaxCodeLengthCodeLength; i++ {
		symbol += int(count[bits])
		offset[bits] = symbol
		bits++
	}

	/* Symbols with code length 0 are placed after all other symbols. */
	offset[0] = codeLengthCodes - 1

	/* Sort symbols by length, by symbol order within each length. */
	symbol = codeLengthCodes

	for {
		var i int
		for i = 0; i < 6; i++ {
			symbol--
			sorted[offset[code_lengths[symbol]]] = symbol
			offset[code_lengths[symbol]]--
		}
		if symbol == 0 {
			break
		}
	}

	table_size = 1 << huffmanMaxCodeLengthCodeLength

	/* Special case: all symbols but one have 0 code length. */
	if offset[0] == 0 {
		code = constructHuffmanCode(0, uint16(sorted[0]))
		for key = 0; key < uint64(table_size); key++ {
			table[key] = code
		}

		return
	}

	/* Fill in table. */
	key = 0

	key_step = reverseBitsLowest
	symbol = 0
	bits = 1
	step = 2
	for {
		for bits_count = int(count[bits]); bits_count != 0; bits_count-- {
			code = constructHuffmanCode(byte(bits), uint16(sorted[symbol]))
			symbol++
			replicateValue(table[reverseBits8(key):], step, table_size, code)
			key += key_step
		}

		step <<= 1
		key_step >>= 1
		bits++
		if bits > huffmanMaxCodeLengthCodeLength {
			break
		}
	}
}

func buildHuffmanTable(root_table []huffmanCode, root_bits int, symbol_lists symbolList, count []uint16) uint32 {
	var code huffmanCode /* current table entry */ /* next available space in table */ /* current code length */ /* symbol index in original or sorted table */ /* prefix code */ /* prefix code addend */ /* 2nd level table prefix code */ /* 2nd level table prefix code addend */ /* step size to replicate values in current table */ /* key length of current table */ /* size of current table */ /* sum of root table size and 2nd level table sizes */
	var table []huffmanCode
	var len int
	var symbol int
	var key uint64
	var key_step uint64
	var sub_key uint64
	var sub_key_step uint64
	var step int
	var table_bits int
	var table_size int
	var total_size int
	var max_length int = -1
	var bits int
	var bits_count int

	assert(root_bits <= reverseBitsMax)
	assert(huffmanMaxCodeLength-root_bits <= reverseBitsMax)

	for symbolListGet(symbol_lists, max_length) == 0xFFFF {
		max_length--
	}
	max_length += huffmanMaxCodeLength + 1

	table = root_table
	table_bits = root_bits
	table_size = 1 << uint(table_bits)
	total_size = table_size

	/* Fill in the root table. Reduce the table size to if possible,
	   and create the repetitions by memcpy. */
	if table_bits > max_length {
		table_bits = max_length
		table_size = 1 << uint(table_bits)
	}

	key = 0
	key_step = reverseBitsLowest
	bits = 1
	step = 2
	for {
		symbol = bits - (huffmanMaxCodeLength + 1)
		for bits_count = int(count[bits]); bits_count != 0; bits_count-- {
			symbol = int(symbolListGet(symbol_lists, symbol))
			code = constructHuffmanCode(byte(bits), uint16(symbol))
			replicateValue(table[reverseBits8(key):], step, table_size, code)
			key += key_step
		}

		step <<= 1
		key_step >>= 1
		bits++
		if bits > table_bits {
			break
		}
	}

	/* If root_bits != table_bits then replicate to fill the remaining slots. */
	for total_size != table_size {
		copy(table[table_size:], table[:uint(table_size)])
		table_size <<= 1
	}

	/* Fill in 2nd level tables and add pointers to root table. */
	key_step = reverseBitsLowest >> uint(root_bits-1)

	sub_key = reverseBitsLowest << 1
	sub_key_step = reverseBitsLowest
	len = root_bits + 1
	step = 2
	for ; len <= max_length; len++ {
		symbol = len - (huffmanMaxCodeLength + 1)
		for ; count[len] != 0; count[len]-- {
			if sub_key == reverseBitsLowest<<1 {
				table = table[table_size:]
				table_bits = nextTableBitSize(count, int(len), root_bits)
				table_size = 1 << uint(table_bits)
				total_size += table_size
				sub_key = reverseBits8(key)
				key += key_step
				root_table[sub_key] = constructHuffmanCode(byte(table_bits+root_bits), uint16(uint64(uint(-cap(table)+cap(root_table)))-sub_key))
				sub_key = 0
			}

			symbol = int(symbolListGet(symbol_lists, symbol))
			code = constructHuffmanCode(byte(len-root_bits), uint16(symbol))
			replicateValue(table[reverseBits8(sub_key):], step, table_size, code)
			sub_key += sub_key_step
		}

		step <<= 1
		sub_key_step >>= 1
	}

	return uint32(total_size)
}

func buildSimpleHuffmanTable(table []huffmanCode, root_bits int, val []uint16, num_symbols uint32) uint32 {
	var table_size uint32 = 1
	var goal_size uint32 = 1 << uint(root_bits)
	switch num_symbols {
	case 0:
		table[0] = constructHuffmanCode(0, val[0])

	case 1:
		if val[1] > val[0] {
			table[0] = constructHuffmanCode(1, val[0])
			table[1] = constructHuffmanCode(1, val[1])
		} else {
			table[0] = constructHuffmanCode(1, val[1])
			table[1] = constructHuffmanCode(1, val[0])
		}

		table_size = 2

	case 2:
		table[0] = constructHuffmanCode(1, val[0])
		table[2] = constructHuffmanCode(1, val[0])
		if val[2] > val[1] {
			table[1] = constructHuffmanCode(2, val[1])
			table[3] = constructHuffmanCode(2, val[2])
		} else {
			table[1] = constructHuffmanCode(2, val[2])
			table[3] = constructHuffmanCode(2, val[1])
		}

		table_size = 4

	case 3:
		var i int
		var k int
		for i = 0; i < 3; i++ {
			for k = i + 1; k < 4; k++ {
				if val[k] < val[i] {
					var t uint16 = val[k]
					val[k] = val[i]
					val[i] = t
				}
			}
		}

		table[0] = constructHuffmanCode(2, val[0])
		table[2] = constructHuffmanCode(2, val[1])
		table[1] = constructHuffmanCode(2, val[2])
		table[3] = constructHuffmanCode(2, val[3])
		table_size = 4

	case 4:
		if val[3] < val[2] {
			var t uint16 = val[3]
			val[3] = val[2]
			val[2] = t
		}

		table[0] = constructHuffmanCode(1, val[0])
		table[1] = constructHuffmanCode(2, val[1])
		table[2] = constructHuffmanCode(1, val[0])
		table[3] = constructHuffmanCode(3, val[2])
		table[4] = constructHuffmanCode(1, val[0])
		table[5] = constructHuffmanCode(2, val[1])
		table[6] = constructHuffmanCode(1, val[0])
		table[7] = constructHuffmanCode(3, val[3])
		table_size = 8
	}

	for table_size != goal_size {
		copy(table[table_size:], table[:uint(table_size)])
		table_size <<= 1
	}

	return goal_size
}