//
// optblock.c
//
// Outputting blocks
//
#include "deflate.h"
#include <string.h>
#include <stdio.h>
#include <crtdbg.h>
#include "maketbl.h"


//
// Decode a recorded literal
//
#define DECODE_LITERAL(slot) \
	slot = encoder->recording_literal_tree_table[read_bitbuf & REC_LITERALS_DECODING_TABLE_MASK]; \
	while (slot < 0) \
	{  \
		unsigned long mask = 1 << REC_LITERALS_DECODING_TABLE_BITS; \
		do \
		{ \
			slot = -slot; \
			if ((read_bitbuf & mask) == 0) \
				slot = encoder->recording_literal_tree_left[slot]; \
			else \
				slot = encoder->recording_literal_tree_right[slot]; \
			mask <<= 1; \
		} while (slot < 0); \
	}


//
// Decode a recorded distance slot
//
#define DECODE_POS_SLOT(slot) \
	slot = encoder->recording_dist_tree_table[read_bitbuf & REC_DISTANCES_DECODING_TABLE_MASK]; \
	while (slot < 0) \
	{  \
		unsigned long mask = 1 << REC_DISTANCES_DECODING_TABLE_BITS; \
		do \
		{ \
			slot = -slot; \
			if ((read_bitbuf & mask) == 0) \
				slot = encoder->recording_dist_tree_left[slot]; \
			else \
				slot = encoder->recording_dist_tree_right[slot]; \
			mask <<= 1; \
		} while (slot < 0); \
	}


//
// Remove count bits from the bit buffer
//
#define DUMP_READBUF_BITS(count) \
	read_bitbuf >>= count; \
	read_bitcount -= count;


//
// Read more bits into the read buffer if our bit buffer if we need to
//
#define CHECK_MORE_READBUF() \
	if (read_bitcount <= 0) \
	{ \
		read_bitbuf |= ((*read_bufptr++) << (read_bitcount+16)); \
		read_bitcount += 8; \
		if (read_bitcount <= 0) \
		{ \
			read_bitbuf |= ((*read_bufptr++) << (read_bitcount+16)); \
			read_bitcount += 8; \
		} \
	}


// output an element from the literal tree
#define OUTPUT_LITERAL(element) \
{ \
	_ASSERT(encoder->literal_tree_len[element] != 0); \
	outputBits(context, encoder->literal_tree_len[element], encoder->literal_tree_code[element]); \
}


// output an element from the distance tree
#define OUTPUT_DIST_SLOT(element) \
{ \
	_ASSERT(encoder->dist_tree_len[element] != 0); \
	outputBits(context, encoder->dist_tree_len[element], encoder->dist_tree_code[element]); \
}



//
// Output a dynamic block
//
static BOOL OptimalEncoderOutputDynamicBlock(t_encoder_context *context)
{
	unsigned long	read_bitbuf;
	int				read_bitcount;
	byte *			read_bufptr;
    t_optimal_encoder *encoder = context->optimal_encoder;

	if (context->state == STATE_NORMAL)
	{
		//
		// If we haven't started to output a block yet
		//
        read_bufptr     = encoder->lit_dist_buffer;
		read_bitbuf		= 0;
		read_bitcount	= -16;

		read_bitbuf |= ((*read_bufptr++) << (read_bitcount+16)); 
		read_bitcount += 8;

		read_bitbuf |= ((*read_bufptr++) << (read_bitcount+16)); 
		read_bitcount += 8;

		context->outputting_block_bitbuf		= read_bitbuf;
		context->outputting_block_bitcount		= read_bitcount;
		context->outputting_block_bufptr		= read_bufptr;

		outputBits(context, 1, 0); // "final" block flag
		outputBits(context, 2, BLOCKTYPE_DYNAMIC); 

		context->state = STATE_OUTPUTTING_TREE_STRUCTURE;
	}

	if (context->state == STATE_OUTPUTTING_TREE_STRUCTURE)
	{
		//
		// Make sure there is enough room to output the entire tree structure at once
		//
		if (context->output_curpos > context->output_endpos - MAX_TREE_DATA_SIZE)
		{
            _ASSERT(0); // not enough room to output tree structure, fatal error!
			return FALSE;
		}

		outputTreeStructure(context, encoder->literal_tree_len, encoder->dist_tree_len);

		context->state = STATE_OUTPUTTING_BLOCK;
	}

	_ASSERT(context->state == STATE_OUTPUTTING_BLOCK);

	// load state into local variables
	read_bufptr		= context->outputting_block_bufptr;
	read_bitbuf		= context->outputting_block_bitbuf;
	read_bitcount	= context->outputting_block_bitcount;

	// output literals
	while (context->outputting_block_current_literal < context->outputting_block_num_literals)
	{
		int literal;

		// break when we get near the end of our output buffer
		if (context->output_curpos >= context->output_near_end_threshold)
			break;

		DECODE_LITERAL(literal);
		DUMP_READBUF_BITS(encoder->recording_literal_tree_len[literal]);
		CHECK_MORE_READBUF();

		if (literal < NUM_CHARS)
		{
			// it's a char
			OUTPUT_LITERAL(literal);
		}
		else
		{
			// it's a match
			int len_slot, pos_slot, extra_pos_bits;

			// literal == len_slot + (NUM_CHARS+1)
			_ASSERT(literal != END_OF_BLOCK_CODE);

			OUTPUT_LITERAL(literal);

			len_slot = literal - (NUM_CHARS+1);

			//
			// extra_length_bits[len_slot] > 0 when len_slot >= 8
			// (except when length is MAX_MATCH).
			//
			if (len_slot >= 8)
			{
				int extra_bits = g_ExtraLengthBits[len_slot];

				if (extra_bits > 0)
				{
					unsigned int extra_data = read_bitbuf & ((1 << extra_bits)-1);

					outputBits(context, extra_bits, extra_data);
					
					DUMP_READBUF_BITS(extra_bits);
					CHECK_MORE_READBUF();
				}
			}

			DECODE_POS_SLOT(pos_slot);
			DUMP_READBUF_BITS(encoder->recording_dist_tree_len[pos_slot]);
			CHECK_MORE_READBUF();

			_ASSERT(pos_slot < 30);

			OUTPUT_DIST_SLOT(pos_slot);

			extra_pos_bits = g_ExtraDistanceBits[pos_slot];

			if (extra_pos_bits > 0)
			{
				unsigned int extra_data = read_bitbuf & ((1 << extra_pos_bits)-1);

				outputBits(context, extra_pos_bits, extra_data);

				DUMP_READBUF_BITS(extra_pos_bits);
				CHECK_MORE_READBUF();
			}
		}

		context->outputting_block_current_literal++;
	}

	// did we output all of our literals without running out of output space?
	if (context->outputting_block_current_literal >= context->outputting_block_num_literals)
	{
		// output the code signifying end-of-block
		OUTPUT_LITERAL(END_OF_BLOCK_CODE);

		// reset state
		context->state = STATE_NORMAL;
	}
	else
	{
		context->outputting_block_bitbuf	= read_bitbuf;
		context->outputting_block_bitcount	= read_bitcount;
		context->outputting_block_bufptr	= read_bufptr;
		context->state					    = STATE_OUTPUTTING_BLOCK;
	}

    return TRUE;
}



//
// Output a block.  This routine will resume outputting a block that was already being
// output if state != STATE_NORMAL.
//
BOOL OptimalEncoderOutputBlock(t_encoder_context *context)
{
    t_optimal_encoder *encoder = context->optimal_encoder;

    _ASSERT(encoder != NULL);

	//
	// The tree creation routines cannot >= 65536 literals.
	//
	_ASSERT(context->outputting_block_num_literals < 65536);

	if (context->state == STATE_NORMAL)
	{
        //
		// Start outputting literals and distances from the beginning
		//
		context->outputting_block_current_literal = 0;
	
		//
		// Nothing to output?  Then return
		//
		if (context->outputting_block_num_literals == 0)
			return TRUE;

		// make decoding table so that we can decode recorded items
		makeTable(
			MAX_LITERAL_TREE_ELEMENTS,
			REC_LITERALS_DECODING_TABLE_BITS,
			encoder->recording_literal_tree_len,
			encoder->recording_literal_tree_table,
			encoder->recording_literal_tree_left,
			encoder->recording_literal_tree_right
		);

		makeTable(
			MAX_DIST_TREE_ELEMENTS,
			REC_DISTANCES_DECODING_TABLE_BITS,
			encoder->recording_dist_tree_len,
			encoder->recording_dist_tree_table,
			encoder->recording_dist_tree_left,
			encoder->recording_dist_tree_right
		);

		// now make the trees used for encoding
	    makeTree(
    		MAX_LITERAL_TREE_ELEMENTS, 
	    	15, 
    		encoder->literal_tree_freq, 
	    	encoder->literal_tree_code,
    		encoder->literal_tree_len
    	);

	    makeTree(
    		MAX_DIST_TREE_ELEMENTS, 
	    	15, 
    		encoder->dist_tree_freq, 
	    	encoder->dist_tree_code,
    		encoder->dist_tree_len
    	);
	}

	//
	// Try outputting as a dynamic block
	//
	if (OptimalEncoderOutputDynamicBlock(context) == FALSE)
    {
        return FALSE;
    }

    if (context->state == STATE_NORMAL)
    {
   	    encoder->recording_bufptr           = context->optimal_encoder->lit_dist_buffer;
        encoder->recording_bitbuf           = 0;
        encoder->recording_bitcount         = 0;

		context->outputting_block_num_literals = 0;

		// make sure there are no zero frequency items
		NormaliseFrequencies(encoder->literal_tree_freq, encoder->dist_tree_freq);

		// make tree for recording new items
		makeTree(
			MAX_DIST_TREE_ELEMENTS, 
			RECORDING_DIST_MAX_CODE_LEN,
			encoder->dist_tree_freq, 
			encoder->recording_dist_tree_code, 
			encoder->recording_dist_tree_len
		);

		makeTree(
			MAX_LITERAL_TREE_ELEMENTS, 
			RECORDING_LIT_MAX_CODE_LEN,
			encoder->literal_tree_freq, 
			encoder->recording_literal_tree_code, 
			encoder->recording_literal_tree_len
		);

		OptimalEncoderZeroFrequencyCounts(encoder);
    }

    return TRUE;
}
