/*
 * decblk.c
 *
 * main decoder module
 */
#include "decoder.h"


/* local function prototypes */
static int decode_block(
	t_decoder_context	*context, 
	lzx_block_type		block_type, 
	long				bufpos, 
    long                amount_to_decode
);


/*
 * Decode a block type
 */
static int decode_block(
	t_decoder_context	*context, 
	lzx_block_type		block_type, 
	long				bufpos, 
    long                amount_to_decode
)
{
    int result;

	if (block_type == BLOCKTYPE_ALIGNED)
        result = decode_aligned_offset_block(context, bufpos, (int) amount_to_decode);
	else if (block_type == BLOCKTYPE_VERBATIM)
        result = decode_verbatim_block(context, bufpos, (int) amount_to_decode);
	else if (block_type == BLOCKTYPE_UNCOMPRESSED)
        result = decode_uncompressed_block(context, bufpos, (int) amount_to_decode);
	else /* no other block types exist */
		result = -1;

	return result;
}



/*
 * Main decode entrypoint
 */
long NEAR decode_data(t_decoder_context *context, long bytes_to_decode)
{
	ulong			amount_can_decode;
	long			total_decoded;

	total_decoded = 0;

	while (bytes_to_decode > 0)          
	{
		if (context->dec_decoder_state == DEC_STATE_START_NEW_BLOCK)
		{
			ulong	temp1;                                                                                           
			ulong	temp2;
			ulong	temp3;
			bool	do_translation;

			/*
			 * If this is the first time this group, then get the
			 * file size for translation.
			 */
			if (context->dec_first_time_this_group)
			{
				context->dec_first_time_this_group = false;

                do_translation = (bool) getbits(context, 1);

				if (do_translation)
				{
					ulong high, low;

					high = getbits(context, 16);
					low  = getbits(context, 16);
					context->dec_current_file_size = (high<<16)|low;
				}
				else
				{
					context->dec_current_file_size = 0;
				}
			}

            /*
             * If the last block we decoded was uncompressed, then
             * we need to skip the pad byte (if it exists), and
             * initialise the decoder's bit buffer
             */
            if (context->dec_block_type == BLOCKTYPE_UNCOMPRESSED)
            {
                /*
                 * If block size was odd, a pad byte is required
                 */
                if (context->dec_original_block_size & 1)
                {
                    if (context->dec_input_curpos < context->dec_end_input_pos)
                        context->dec_input_curpos++;
                }

                /* so that initialise_decoder_bitbuf() will succeed */
                context->dec_block_type = BLOCKTYPE_INVALID;

                initialise_decoder_bitbuf(context);
            }

			/* get the block type */
            context->dec_block_type = (lzx_block_type) getbits(context, 3);

			/* get size of block (in uncompressed bytes) to decode */
			temp1 = getbits(context, 8);
			temp2 = getbits(context, 8);
			temp3 = getbits(context, 8);

			/*
			 * How large is the block we're going to decode?
			 * It can be from 0...16777215 bytes (16MB)
			 */
            context->dec_block_size =
            context->dec_original_block_size = (temp1<<16) + (temp2<<8) + (temp3);

			/* if block is an aligned type, read the aligned offset tree */
			if (context->dec_block_type == BLOCKTYPE_ALIGNED)
				read_aligned_offset_tree(context);

			/* read trees */
			if (context->dec_block_type == BLOCKTYPE_VERBATIM || 
      			context->dec_block_type == BLOCKTYPE_ALIGNED)
			{
	 			/*	backup old trees */
	 			memcpy(
                    context->dec_main_tree_prev_len, 
                    context->dec_main_tree_len, 
	 				MAIN_TREE_ELEMENTS
	 			);

	 			memcpy(
                    context->dec_secondary_length_tree_prev_len,
                    context->dec_secondary_length_tree_len,
	 				NUM_SECONDARY_LENGTHS
	 			);

				read_main_and_secondary_trees(context);
			}
			else if (context->dec_block_type == BLOCKTYPE_UNCOMPRESSED)
			{
                if (handle_beginning_of_uncompressed_block(context) == false)
                    return -1;
			}
			else 
			{
				/* no other block types are supported at this time */
                return -1;
			}

			context->dec_decoder_state = DEC_STATE_DECODING_DATA;
		}

		/*
		 * Keep decoding until the whole block has been decoded
		 */
		while ((context->dec_block_size > 0) && (bytes_to_decode > 0))
		{
            int decode_residue;

			amount_can_decode = (((context->dec_block_size) < (bytes_to_decode)) ? (context->dec_block_size) : (bytes_to_decode));
		
	 		/* shouldn't happen */
	 		if (amount_can_decode == 0)
                return -1;

 			decode_residue = decode_block(
				context, 
				context->dec_block_type, 
				context->dec_bufpos, 
				amount_can_decode
			);

	 		/*
	 		 * We should have decoded exactly the amount we wanted,
			 * since the encoder makes sure that no matches span 32K
			 * boundaries.
			 *
	 		 * If the data was corrupted, it's possible that we decoded
			 * up to MAX_MATCH bytes more than we wanted to.
  			 */
	 		if (decode_residue != 0)
	    	{
				/* error, we didn't decode what we wanted! */
                return -1;
			}

	 		context->dec_block_size -= amount_can_decode;
			bytes_to_decode -= amount_can_decode;
			total_decoded += amount_can_decode;
		}

		if (context->dec_block_size == 0)
		{
			context->dec_decoder_state = DEC_STATE_START_NEW_BLOCK;
		}

		if (bytes_to_decode == 0)
		{
			initialise_decoder_bitbuf(context);
		}
	}

#ifdef BIT16
    copy_data_to_output(
        context,
        total_decoded,
        context->dec_output_buffer
    );
#else
    copy_data_to_output(
		context,
		total_decoded, 
		context->dec_bufpos ? 
			&context->dec_mem_window[context->dec_bufpos - total_decoded] : 
			&context->dec_mem_window[context->dec_window_size - total_decoded]
	);
#endif

    return total_decoded;
}
