// Filename : huff.cpp
//
// This file was initially generated by SpaceStudio but is meant to be modified
// by the user.
#include "huff.h"
#include "spacecomp_pre_cpp.h"

static const DC_HUFFMAN_STRUCTURE ZERO_DC_HUFFMAN_STRUCTURE = {0};
static const AC_HUFFMAN_STRUCTURE ZERO_AC_HUFFMAN_STRUCTURE = {0};

huff::huff(SPACECOMP_CTOR_PARAMS(huff, INDEX))
	: abstract_module(SPACECOMP_MBASE_ARGS(huff, INDEX))
	, SPACECOMP_START_ILIST(huff, INDEX)
	// Other initializers may go here
{
	SPACECOMP_THREAD(huff, INDEX, thread);
	request_stack(16 * 1024);
}

void huff::thread(SPACECOMP_THREAD_PARAMS(huff, INDEX, thread)) {
	spacecomp_thread_initialize();
	JMSG inmsg;
	JMSG outmsg;

	unsigned char m_nbtable; // add to member variable
	unsigned short m_nbMCU;

	// initiate state of decompression
	m_state = JPEG_STARTED;

	m_nbtable = 0; // add to member variable
	m_nbMCU = 0;
	while (1) {
		const bool initializing = spacecomp_thread_loop_start();
		if (initializing) {
			// reset to 0 structures and data related to jpeg decompression
			initialize();
		}
		hw_compute_latency(2);

		ModuleRead(EXTR0_ID, SPACE_BLOCKING, &inmsg);

		switch (inmsg.command) {
		case SCMD_HUFFMAN_DHT: // read for huffman tables

			m_inputAdd = inmsg.param0;

			read_dht_marker();

			m_nbtable++;

			if (is_verbose())
				SpacePrint("HUFF: retreived DHT table no %d sucecssfully! Sending back address 0x%X\n", m_nbtable, m_inputAdd);

			// positive ack - return pointer
			outmsg.command = SCMD_ACK;
			outmsg.param0 = m_inputAdd;
			hw_compute_latency(1);
			ModuleWrite(EXTR0_ID, SPACE_BLOCKING, &outmsg);

			lsize = 0;
			lcode = 0;
			ldc[0] = ldc[1] = ldc[2] = 0;

			m_nbMCU = 0;

			break;

			//
		case SCMD_HUFFMAN_MCU:

			if (is_verbose())
					SpacePrint("HUFF: HUFFMAN_MCU received from EXTRACTOR - launching Huffman decompression\n");

			m_currentHuffmanComponent = inmsg.param0;
			m_inputAdd = inmsg.param1;

			launch_huffman();

			m_nbMCU++;
			//SpacePrint("%s: HUFFMAN has retreived MCU block no %d successfully!\n", this->name(), m_nbMCU);

			// positive ack - return pointer
			outmsg.command = SCMD_ACK;
			outmsg.param0 = m_inputAdd;
			hw_compute_latency(1);
			ModuleWrite(EXTR0_ID, SPACE_BLOCKING, &outmsg);

			if (is_verbose())
				SpacePrint("HUFF: ACK returned to EXTRACTOR\n");

			m_nbtable = 0;

			break;

		 case SCMD_END_IMAGE:
			hw_compute_latency(1);
			ModuleWrite(IQTZ0_ID, SPACE_BLOCKING, &inmsg);
			break;
		}
	}
}

uint8_t huff::read_8_bits() {
	uint8_t inputValue;
	SPACE_ALIGNED uint8_t inputBuffer[4];

	uint8_t alignment = (m_inputAdd & 0x3);
	DeviceRead(JPEGRAM_ID, m_inputAdd & 0xFFFFFFFC, (uint32_t*)inputBuffer);
	inputValue = inputBuffer[alignment];

	m_inputAdd+=1;

	return inputValue;

}

/*------------------------------------------------------------------------------
* read_16_bits()
*
* Read 16 bits of data from jpeg chunck
*
* ret:	16 bits value read
*------------------------------------------------------------------------------*/
uint16_t huff::read_16_bits() {
	uint16_t value;
	uint8_t reader1;
	uint8_t reader2;


	reader1 = read_8_bits();
	reader2 = read_8_bits();

	value = reader1 << 8;
	value = value | reader2;

	return (value);
}



////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////  HUFFMAN THREADS AND OPERATIONS /////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

/*------------------------------------------------------------------------------
* thread_huffman()
*
* This method extracts the jpeg data from memory and uncompress it using
* jpeg huffman decompression. Uncompressed data is grouped in blocks of
* 64 pxels and transmitted to inverse_quantize process/module
*
* This was originally a thread which was transformed into a single function
* thread code has been commented out
*
*--------------- ---------------------------------------------------------------*/
uint8_t huff::launch_huffman() {
	SPACE_ALIGNED int16_t block[64];
	unsigned char j;
	unsigned char selector;
	unsigned char component_index; // value changes from 0 to 2 ==> 3 possibilities

	signed short dc, ac;
	unsigned char coefficient;
	uint16_t huffcode;
	unsigned char index;
	unsigned char huffval;
	unsigned char huffsize;
	unsigned char run_length, size;

    // clear buffer we use
    for (j=0; j<64; j++)
        block[j] = 0;

    //
    // PROCESS DC COMPONENT
    //
    // 1. READ COMPONENT INDEX EITHER LUMINANCE or CHROMINANCE
    component_index = m_currentHuffmanComponent;

    // 2. READ TABLE ELEMENTS HERE
    if (component_index == 0)
        selector = HUFFTABLE_DCL;
    else
        selector = HUFFTABLE_DCC;

    // READ 16 BITS FROM IMAGE FIFO
    if (lsize < 16) {
        lcode |= comm_read_16huff_bits()  << (16 - lsize);
        lsize += 16;
    }

    huffsize = getHuffmanTableElement(selector, HUFF_HUFFSIZE, lcode >> (32 - NBITS));

    if (huffsize)
        huffcode = lcode >> (32 - huffsize);
    else {
		for(huffsize = NBITS + 1; huffsize <= 16; huffsize++)  {
            huffcode = lcode >> (32 - huffsize);
			if(huffcode <= getHuffmanTableElement(selector, HUFF_MAXCODE, huffsize)) {
				break;
			}
        }

        if (huffsize > 16)
            return ERROR_INVALID_CODE;
    }

    index = huffcode + getHuffmanTableElement(selector, HUFF_MINCODE, huffsize);
    huffval = getHuffmanTableElement(selector, HUFF_HUFFVAL, index);

    lcode <<= huffsize;
    lsize -= huffsize;

    if (lsize < huffval) {
        lcode |= comm_read_16huff_bits() << (16 - lsize);
        lsize += 16;
    }

    if (huffval == 0)
        dc = 0;
    else
        dc = lcode >> (32 - huffval);

    lcode <<= huffval;
    lsize -= huffval;

    if (dc < (1 << (huffval - 1)))
        dc += (- 1 << huffval) + 1;

    //
    // Write DC element into output block
    //

    block [0] = dc + ldc [component_index];

    ldc [component_index] = block[0];


    //
    // PROCESS AC COMPONENT
    //

    if (component_index == 0)
        selector = HUFFTABLE_ACL;
    else
        selector = HUFFTABLE_ACC;

    for (coefficient =  1; coefficient < 64; coefficient ++) {
        if (lsize < 16)
        {
            lcode |= comm_read_16huff_bits() << (16 - lsize);
            lsize += 16;
        }

        huffsize = getHuffmanTableElement(selector, HUFF_HUFFSIZE, lcode >> (32 - NBITS));

        if (huffsize)
            huffcode = lcode >> (32 - huffsize);
        else {
            for(huffsize = NBITS + 1; huffsize <= 16; huffsize++) {
				huffcode = lcode >> (32 - huffsize);
				if(huffcode <= getHuffmanTableElement(selector, HUFF_MAXCODE, huffsize)) {
					break;
				}
			}

            if (huffsize > 16)
                return ERROR_INVALID_CODE;
        }

        index = huffcode + getHuffmanTableElement(selector, HUFF_MINCODE, huffsize);
        huffval = getHuffmanTableElement(selector, HUFF_HUFFVAL, index);

        lcode <<= huffsize;
        lsize -= huffsize;

        run_length = huffval >> 4;
        size = huffval & 15;

        if (size) {
            coefficient += run_length;

            if (lsize < size) {
                lcode |= comm_read_16huff_bits() << (16 - lsize);

                lsize += 16;
            }

            ac = lcode >> (32 - size);

            lcode <<= size;
            lsize -= size;

            if (ac < (1 << (size - 1)))
                ac += (- 1 << size) + 1;

            block [coefficient] = ac;
        }
        else {
            if (run_length != 15)
                break;

            coefficient += 15;
        }
    }

    hw_compute_latency(25);

    communication_write64pixelblock(component_index, block);

	return SUCCESS;

}

/*------------------------------------------------------------------------------
* communication_write64pixelblock()
*
* prepares communication of a 64 element block to IJPEG module for inverse
* quantization and inverse DCT
*
* input:	component_index is parameter2 to be transferred to IJPEG module
* input:	pblock is a pointer to block of data to be transferred
*
* ret:		SUCCESS
*------------------------------------------------------------------------------*/
void huff::communication_write64pixelblock(unsigned int component_index, int16_t *pblock) {
	if (is_verbose())
		SpacePrint("HUFF: Transferring 64 decompressed pixels to IQTZ\n");

	//
	// send type of table to be transferred
	//

	JMSG myjpegmessage;
	myjpegmessage.command	= SCMD_BEGIN_COMP_DATA_TRANSMISSION;
	myjpegmessage.param0	= component_index;

	hw_compute_latency(1);

	ModuleWrite(IQTZ0_ID, SPACE_BLOCKING, &myjpegmessage);

	hw_compute_latency(1);

	//
	// multiple byte transfer of table
	//
	hw_compute_latency(32);
	ModuleWrite(IQTZ0_ID, SPACE_BLOCKING, (uint32_t*)pblock, 32);
}


/*------------------------------------------------------------------------------
* comm_read_16huff_bits()
*
*
*
* ret:
*------------------------------------------------------------------------------*/
uint16_t huff::comm_read_16huff_bits() {
	uint8_t val1, val2;

	// read first value

	val1 = read_8_bits();
	if (val1 == 0xff)
		read_8_bits();

	val2 = read_8_bits();
	if (val2 == 0xff)
		read_8_bits();

	return (val1 << 8) | val2;
}

/*------------------------------------------------------------------------------
* getHuffmanTableElement()
*
*
*
* ret:
*------------------------------------------------------------------------------*/

int32_t huff::getHuffmanTableElement(int selector, int id, int index) {
	int ACDC = 0;
	int MCU_TYPE = 0;

	switch ( selector ) {
			case HUFFTABLE_ACL :
				ACDC = AC;
				MCU_TYPE = LUMINANCE;
				break;

			case HUFFTABLE_ACC:
				ACDC = AC;
				MCU_TYPE = CHROMINANCE;
				break;

			case HUFFTABLE_DCL:
				ACDC = DC;
				MCU_TYPE = LUMINANCE;
				break;

			case HUFFTABLE_DCC:
				ACDC = DC;
				MCU_TYPE = CHROMINANCE;
				break;
		}

		switch (id) {
			case HUFF_HUFFVAL:
				if(ACDC == DC)
					return ( dc_huffman_structure[MCU_TYPE].huffval_table[index] );
				else
					return ( ac_huffman_structure[MCU_TYPE].huffval_table[index] );
				break;
			case HUFF_HUFFSIZE:
				if(ACDC == DC)
					return( dc_huffman_structure[MCU_TYPE].huffsize_table[index] );
				else
					return( ac_huffman_structure[MCU_TYPE].huffsize_table[index] );
				break;
			case HUFF_MINCODE:
				if(ACDC == DC)
					return( dc_huffman_structure[MCU_TYPE].valptr_minus_mincode_table[index] );
				else
					return( ac_huffman_structure[MCU_TYPE].valptr_minus_mincode_table[index] );
				break;
			case HUFF_MAXCODE:
				if(ACDC == DC)
					return( dc_huffman_structure[MCU_TYPE].maxcode_table[index] );
				else
					return( ac_huffman_structure[MCU_TYPE].maxcode_table[index] );
				break;
		}

		hw_compute_latency(1);

		// patch to fix
		return 0;
}




/*------------------------------------------------------------------------------
* initialize()
*
*
* ret:
*--------------- ---------------------------------------------------------------*/
void huff::initialize() {
	// initialize to zero arrays and tables
	dc_huffman_structure[0] = dc_huffman_structure[1] = ZERO_DC_HUFFMAN_STRUCTURE;
	ac_huffman_structure[0] = ac_huffman_structure[1] = ZERO_AC_HUFFMAN_STRUCTURE;

	lsize = 0;
	lcode = 0;
	ldc[0] = ldc[1] = ldc[2] = 0;

}


//
// huff::read_huffman_table
//////////////////////////////////////////////////////////////////////////////
///
/// Description
///
/// @param  =>  int32_t*  valptr_minus_mincode_table :
/// @param  =>  int32_t*  maxcode_table :
/// @param  =>  uint8_t*  huffsize_table :
/// @param  =>  uint8_t*  huffval_table :
/// @param  =>  uint16_t&  huffman_table_definition_length :
///
/// @return =>  uint16_t  :
///
/// @note   =>
//////////////////////////////////////////////////////////////////////////////
uint8_t huff::read_huffman_table(signed long* valptr_minus_mincode_table, signed long* maxcode_table, unsigned char* huffsize_table, uint8_t* huffval_table, unsigned short& huffman_table_definition_length) {
	unsigned short i;
	unsigned short code;
	unsigned char j, k, l, index, bits;

	k = 0;

	code = 0;
	index=0;

	for (i = 1; i <= NBITS; i ++) {
		bits = read_8_bits();

		for (j = 1; j <= bits; j++) {
			k++;

			code++;

			for (l = 0; l < 1 << (NBITS - i); l++)
				huffsize_table [index++] = i;
		}

		if (bits) {
			valptr_minus_mincode_table [i] = k - code;
			maxcode_table [i] = code - 1;
		}
		else
			maxcode_table [i] = - 1;

		code <<= 1;
	}

	for (i = index; i < (1 << NBITS); i++)
		huffsize_table [i] = 0;

	for (i=NBITS+1; i<=16; i++) {
		bits = read_8_bits();

		valptr_minus_mincode_table [i] = k - code;

		k += bits;
		code += bits;

		if (bits)
			maxcode_table [i] = code - 1;
		else
			maxcode_table [i] = - 1;

		code <<= 1;
	}

	//maxcode_table [17] = 0x1ffff;

	huffman_table_definition_length -= 1 + 16;

	if (k > 162 || k > huffman_table_definition_length)
		return ERROR_INVALID_NUMBER_OF_HUFFMAN_CODES;

	for (i=0; i<k; i++)
		huffval_table [i] = read_8_bits();

	huffman_table_definition_length -= k;

	return SUCCESS;
}


/*------------------------------------------------------------------------------
* read_dht_marker()
*
* Read Huffman Table  Marker
*
* ret:
*------------------------------------------------------------------------------*/
uint8_t huff::read_dht_marker() {
	unsigned short huffman_table_definition_length;
	unsigned char table_class, huffman_table_destination_identifier;
	unsigned char tmp;

	// first value is size of table (-2 is the size of field "size" written into file)
	huffman_table_definition_length = read_16_bits() - 2;

	if (is_verbose())
		SpacePrint("HUFF: DHT table size 0x%X\n", huffman_table_definition_length+2);

	while (huffman_table_definition_length > 0) {
		//
		// read table class
		//
		tmp = read_8_bits();
		table_class = tmp >> 4;
		if (table_class > 1)
			return ERROR_INVALID_TABLE_CLASS;

		//
		// read table destination identifier
		//
		huffman_table_destination_identifier = tmp & 0xf;
		if (huffman_table_destination_identifier > 3)
			return ERROR_INVALID_TABLE_DESTINATION_IDENTIFIER;

		//
		// fill HUFFMAN table structure fields
		//
		if(table_class == DC) {
			uint8_t status = read_huffman_table(
				dc_huffman_structure[huffman_table_destination_identifier].valptr_minus_mincode_table,
				dc_huffman_structure[huffman_table_destination_identifier].maxcode_table,
				dc_huffman_structure[huffman_table_destination_identifier].huffsize_table,
				dc_huffman_structure[huffman_table_destination_identifier].huffval_table,
				huffman_table_definition_length);
			if(status != SUCCESS) return status;
        } else {
			uint8_t status = read_huffman_table(
				ac_huffman_structure[huffman_table_destination_identifier].valptr_minus_mincode_table,
				ac_huffman_structure[huffman_table_destination_identifier].maxcode_table,
				ac_huffman_structure[huffman_table_destination_identifier].huffsize_table,
				ac_huffman_structure[huffman_table_destination_identifier].huffval_table,
				huffman_table_definition_length);
			if(status != SUCCESS) return status;
		}
	}

	hw_compute_latency(huffman_table_definition_length+1);

	return SUCCESS;
}

#include "spacecomp_post_cpp.h"
