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

static const unsigned char ic1 = 251;
static const unsigned char is1 = 50;
static const unsigned char ic3 = 213;
static const unsigned char is3 = 142;
static const unsigned short ir2c6 = 277;
static const unsigned short ir2s6 = 669;
static const unsigned char ir2 = 181;

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

void idct::thread(SPACECOMP_THREAD_PARAMS(idct, INDEX, thread)) {
	spacecomp_thread_initialize();
	JMSG input_message, output_message;

	unsigned short blockCounter;

	//variables for INVERSE DCT computation
	int16_t* dataptr;

	// maximum intermediate bit width is COL_SHIFT (11) + SAMPLE_SIZE(8) + 3
	// "Systematic Approach of Fixed Point 8x8 IDCT and DCT Design and Implementation"
	signed long x0, x1, x2, x3, x4, x5, x7, x8;
	signed long x6;

	// private member initailization
	unsigned char m_mcuTransferred;

	m_mcuTransferred		= 0;

	blockCounter = 0;
	while (1) {
		const bool initializing = spacecomp_thread_loop_start();
		if (initializing) { }
		if (is_verbose())
	        SpacePrint("IDCT: waiting for command from IQTZ\n");


		//
		// wait for a block to be ready from IQUANT
		//
		ModuleRead(IQTZ0_ID, SPACE_BLOCKING, &input_message);

	    hw_compute_latency(1);

		// this is the first time - store working address
        if (input_message.param0 != 0)  {
			m_lastMCUStartAddress = input_message.param0;		//Stored for communicating to Y2R module
			m_currentTempBufferAddress = input_message.param0;	//Local Working pointer

			// reset members
			blockCounter = 0;

            if (is_verbose())
                SpacePrint("IDCT: Starting New Image at %x\n", m_lastMCUStartAddress);
		}

		if(input_message.command == SCMD_START_IDCT) {
		    if (is_verbose())
		        SpacePrint("IDCT: START_IDCT received - reading 64 pixels from IQTZ\n");

            //receiving of the 64-elements dequantized block from the IQUANT module
			ModuleRead(IQTZ0_ID, SPACE_BLOCKING, (uint32_t*)m_interblock, 64/2);


			if (is_verbose())
		        SpacePrint("IDCT: 64 pixels received - processing IDCT\n");

			//--------------------------------------------------
			//--------- INVERSE DCT computation --------------//
			//--------------------------------------------------

			// setting ptr to beginning of data array prepared by inverse_quantize
			dataptr = &m_interblock[0];

			for (uint8_t k = 0; k < 8; k ++) {
				x0 = (signed short)(dataptr [0]) << 9;	// 20
				x1 = (signed short)(dataptr [1]) << 7;	// 18
				x2 = (signed short)(dataptr [2]);		// 11
				x3 = (signed short)(dataptr [3]) * ir2;	// 19
				x4 = (signed short)(dataptr [4]) << 9;	// 20
				x5 = (signed short)(dataptr [5]) * ir2;	// 19
				x6 = (signed short)(dataptr [6]);		// 11
				x7 = (signed short)(dataptr [7]) << 7;	// 18
				x8 = x7 + x1;							// 19
				x1 -= x7;								// 19

				x7 = x0 + x4;							// 21
				x0 -= x4;								// 21
				x4 = x1 + x5;							// 20
				x1 -= x5;								// 20
				x5 = x3 + x8;							// 20
				x8 -= x3;								// 20
				x3 = ir2c6 * (x2 + x6);					// 21
				x6 = x3 + (- ir2c6 - ir2s6) * x6;		// 22
				x2 = x3 + (- ir2c6 + ir2s6) * x2;		// 22

				x3 = x7 + x2;							// 23
				x7 -= x2;								// 23
				x2 = x0 + x6;							// 23
				x0 -= x6;								// 23
				x6 = ic3 * (x4 + x5);					// 29
				x5 = (x6 + (- ic3 - is3) * x5) >> 6;	// 24
				x4 = (x6 + (- ic3 + is3) * x4) >> 6;	// 22
				x6 = ic1 * (x1 + x8);					// 28
				x1 = (x6 + (- ic1 - is1) * x1) >> 6;	// 24
				x8 = (x6 + (- ic1 + is1) * x8) >> 6;	// 23

				x7 += 512;								// 24
				x2 += 512;								// 24
				x0 += 512;								// 24
				x3 += 512;								// 24
				dataptr [0] = (int16_t) ((x3 + x4) >> 10);
				dataptr [1] = (int16_t) ((x2 + x8) >> 10);
				dataptr [2] = (int16_t) ((x0 + x1) >> 10);
				dataptr [3] = (int16_t) ((x7 + x5) >> 10);
				dataptr [4] = (int16_t) ((x7 - x5) >> 10);
				dataptr [5] = (int16_t) ((x0 - x1) >> 10);
				dataptr [6] = (int16_t) ((x2 - x8) >> 10);
				dataptr [7] = (int16_t) ((x3 - x4) >> 10);

				dataptr += 8;
			}

			dataptr -= 64;

			for (uint8_t l = 0; l < 8; l ++)  {
				x0 = dataptr [0] << 9;
				x1 = dataptr [8] << 7;
				x2 = dataptr [16];
				x3 = dataptr [24] * ir2;
				x4 = dataptr [32] << 9;
				x5 = dataptr [40] * ir2;
				x6 = dataptr [48];
				x7 = dataptr [56] << 7;
				x8 = x7 + x1;
				x1 -= x7;

				x7 = x0 + x4;
				x0 -= x4;
				x4 = x1 + x5;
				x1 -= x5;
				x5 = x3 + x8;
				x8 -= x3;
				x3 = ir2c6 * (x2 + x6);
				x6 = x3 + (- ir2c6 - ir2s6) * x6;
				x2 = x3 + (- ir2c6 + ir2s6) * x2;

				x3 = x7 + x2;
				x7 -= x2;
				x2 = x0 + x6;
				x0 -= x6;
				x4 >>= 6;
				x5 >>= 6;
				x1 >>= 6;
				x8 >>= 6;
				x6 = ic3 * (x4 + x5);
				x5 = (x6 + (- ic3 - is3) * x5);
				x4 = (x6 + (- ic3 + is3) * x4);
				x6 = ic1 * (x1 + x8);
				x1 = (x6 + (- ic1 - is1) * x1);
				x8 = (x6 + (- ic1 + is1) * x8);

				x7 += 1024;
				x2 += 1024;
				x0 += 1024;
				x3 += 1024;
				dataptr [0] = (int16_t) ((x3 + x4) >> 11);
				dataptr [8] = (int16_t) ((x2 + x8) >> 11);
				dataptr [16] = (int16_t) ((x0 + x1) >> 11);
				dataptr [24] = (int16_t) ((x7 + x5) >> 11);
				dataptr [32] = (int16_t) ((x7 - x5) >> 11);
				dataptr [40] = (int16_t) ((x0 - x1) >> 11);
				dataptr [48] = (int16_t) ((x2 - x8) >> 11);
				dataptr [56] = (int16_t) ((x3 - x4) >> 11);

				dataptr ++;
			}

			// values must be shifted and copied to outblock before written to memory
			for (uint8_t m=0; m<64; m++) {
				m_interblock[m] = m_interblock[m] + 128;

				if (m_interblock[m] < 0)
					m_interblock[m] = 0;
				else if (m_interblock[m] > 255)
					m_interblock[m] = 255;
			}

			hw_compute_latency(100);

			if (is_verbose())
				SpacePrint("IDCT: outputing 64 processed picels to BITMAPRAM\n");


			// write inversed DCT data to memory at tempory address
			// data are transformed from 2 bytes to 4 bytes to correspond to the UTF_Channel operation
			DeviceWrite(BITMAPRAM_ID,  m_currentTempBufferAddress, (uint32_t*)m_interblock, 64/2);
			m_currentTempBufferAddress+=64*BURST_SIZE_SHORT;

			if (is_verbose())
				SpacePrint("IDCT: 64 pixels written to BITMAPRAM\n");


			m_mcuTransferred++;		//to count the number of inverse DCT blocks
			//m_mcuTransferred = 6;		//to count the number of inverse DCT blocks

			hw_compute_latency(2);

			// COMMUNICATION TO Y2R MODULE
			// process total mcu count - when 6 blocks are process,
            // Y1Y2Y3Y4CbCr can be processed
			if (m_mcuTransferred == 6) {
				m_mcuTransferred = 0;

				output_message.command = SCMD_CONVERT_MCU;
				output_message.param0 = false;

				//Collect the total number of dequantified blocks (received in control message)
				blockCounter = input_message.param1;
				// if this is the first block of a series, give Y2V module the address
				if (blockCounter == 6)
				{
					output_message.param1 = m_lastMCUStartAddress;
				}
				else
				{
					output_message.param1 = 0;
				}

				if (is_verbose())
					SpacePrint("IDCT: total of 6 blocks processed - starting Y2R with request for ack:%d and blockCounter:%d\n", output_message.param0, blockCounter);

				// begin a YUV2RGB conversion
				ModuleWrite(Y2R0_ID, SPACE_BLOCKING, &output_message);

			}	//END IF(input_message.command == SCMD_START_IDCT)


		}
        else if (input_message.command == SCMD_END_IMAGE) {
            if (is_verbose())
                SpacePrint("IDCT: End of Image\n", m_lastMCUStartAddress);

            // this is the last block the same series we process
			// request for sync with conv before we start anythign else
			output_message.command = SCMD_CONVERT_MCU;
            output_message.param0 = true; // this is request for ack

			// sync with Y2R module
			ModuleWrite(Y2R0_ID, SPACE_BLOCKING, &output_message);

			hw_compute_latency(1);

			ModuleRead(Y2R0_ID, SPACE_BLOCKING, &input_message);

			if (is_verbose())
				SpacePrint("IDCT: ACK received from Y2R for END_OF_IMAGE command \n");
		}

		hw_compute_latency(1);

		// if a request for ack was requested - process here
		// notiufy IQUANT that we have finished processing the last MCU block
		if (input_message.command == SCMD_CONVERT_MCU_END) {
			if (is_verbose())
				SpacePrint("IDCT: CONVERT_END_MCU received - responding to IQTZ\n");

			output_message.command = SCMD_CONVERT_MCU_END;
			ModuleWrite(IQTZ0_ID, SPACE_BLOCKING, &output_message);
			hw_compute_latency(1);
		}
	}
}

#include "spacecomp_post_cpp.h"
