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

#include "calculate_output_data.h"

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

void calculate_pixel_output::thread(SPACECOMP_THREAD_PARAMS(calculate_pixel_output, INDEX, thread)) {
	spacecomp_thread_initialize();

	calculate_input_struct input;
	int offset = 0;
	unsigned int outputPixel;
	int minR,minG,minB,minA;
	int maxR, maxG,maxB,maxA;
	int sumR,sumG,sumB,sumA;
	int nbPixels;

	while (1) {
		const bool initializing = spacecomp_thread_loop_start();
		if (initializing) {
			// Perform your own initialization communications here as needed.
			// This happens every time the instance is reset, including on
			// initial startup.
			//
			// You may remove this "if" if you do not use it.
		}

		ModuleRead(MJPEG0_ID, SPACE_BLOCKING, &input );

		A: for(unsigned int vi_cnt = 0; vi_cnt < input.data.lumVi; ++vi_cnt)
		{
			#pragma HLS loop_tripcount min=1 max=1 avg=1
			B: for(unsigned int hi_cnt = 0; hi_cnt < input.data.lumHi; ++hi_cnt)
			{
				#pragma HLS loop_tripcount min=2 max=2 avg=2
				C: for(int i = 0; i < input.data.block_height; ++i)
				{
					#pragma HLS loop_tripcount min=8 max=8 avg=8
					D: for (int j = 0; j < input.data.block_width; ++j )
					{
						#pragma HLS loop_tripcount min=8 max=8 avg=8
						if(!input.data.grayScale) {
							offset = 4 * (((input.data.lumVi * input.data.macroLine + vi_cnt) * input.data.block_height * input.data.width) + (i * input.data.width) + (input.data.lumHi * input.data.macroColumn * input.data.block_width) + (hi_cnt * input.data.block_width) + j);
							if (input.data.width_vga != input.data.width)
								offset += 4 * ((int)(offset / 4 / input.data.width))*(input.data.width_vga-input.data.width); // padding to change line

							outputPixel = ycbcr2rgba(Y_LIBU((vi_cnt * input.data.lumVi) + hi_cnt, i, j), input.Cb(((vi_cnt * input.data.block_height + i) / input.data.lumVi), ((hi_cnt * input.data.block_width + j) / input.data.lumHi)), input.Cr(((vi_cnt * input.data.block_height + i) / input.data.lumVi), ((hi_cnt * input.data.block_width + j) / input.data.lumHi)));
							DeviceWrite(VGA_CONTROLLER0_ID, offset, &outputPixel, 1);


							/*static int hei = 0;
							hei++;
							//SpacePrint("pixel: %d - %d - %d", hei, outputPixel,offset);
							SpacePrint("Libu offset - %d  lumVi - %d  macroLine - %d vi_cnt - %d input.block_height - %d WIDTH - %d i - %d  lumHi - %d  macroColumn - %d  hi_cnt - %d j - %d", offset, input.lumVi,  input.macroLine, vi_cnt, input.block_height, input.width, i, input.lumHi,input.macroColumn,hi_cnt,j );

							SpacePrint("\n");*/

							int R = (outputPixel >> 0) & 0xFF;
							int G = (outputPixel >> 8) & 0xFF;
							int B = (outputPixel >> 16) & 0xFF;
							int A = (outputPixel >> 24) & 0xFF;

							sumR += R;
							sumG += G;
							sumB += B;
							sumA += A;

							if(R < minR) minR = R;
							if(G < minG) minG = G;
							if(B < minB) minB = B;
							if(A < minA) minA = A;

							if(R > maxR) maxR = R;
							if(G > maxG) maxG = G;
							if(B > maxB) maxB = B;
							if(A > maxA) maxA = A;

							nbPixels++;
						} else {

								offset = 4 * ((( input.data.lumVi * input.data.macroLine + vi_cnt) * input.data.block_height * input.data.width) + (i * input.data.width) + ( input.data.lumHi * input.data.macroColumn * input.data.block_width) + (hi_cnt * input.data.block_width) + j);

								if (input.data.width_vga != input.data.width)
									offset += 4 * ((int)(offset / 4 / input.data.width))*(input.data.width_vga-input.data.width); // padding to change line

								outputPixel = grayscale2rgba(Y_LIBU((vi_cnt *  input.data.lumVi) + hi_cnt, i, j));
								DeviceWrite(VGA_CONTROLLER0_ID, offset, &outputPixel, 1);
						}
					}
				}
			}
		}

		//From HLS - NO optimization
		hw_compute_latency(10511);
	}
}

unsigned int calculate_pixel_output::ycbcr2rgba(unsigned char y, unsigned char cb, unsigned char cr) {
	unsigned int RGBAWord;
	int R, G, B, A;

	R = (((y << 10) + 1436 * (cr - 128)) >> 10);
	G = (((y << 10) - 352  * (cb - 128) - 732 * (cr - 128)) >> 10);
	B = (((y << 10) + 1815 * (cb - 128)) >> 10);
	A = 255;

	if (R < 0)
		R = 0;
	else if (R > 255)
		R = 255;

	if (G < 0)
		G = 0;
	else if (G > 255)
		G = 255;

	if (B < 0)
		B = 0;
	else if (B > 255)
		B = 255;

	//
	// pixels are stored as such : 0xAABBGGRR
	//
	RGBAWord = A;
	RGBAWord <<= 8;
	RGBAWord |= B;
	RGBAWord <<= 8;
	RGBAWord |= G;
	RGBAWord <<= 8;
	RGBAWord |= R;

	return RGBAWord;
}

unsigned int calculate_pixel_output::grayscale2rgba(unsigned char y) {
	unsigned int RGBAWord;
	int R, G, B, A;

	R = y;
	G = y;
	B = y;
	A = 255;

	//
	// pixels are stored as such : 0xAABBGGRR
	//
	RGBAWord = A;
	RGBAWord <<= 8;
	RGBAWord |= B;
	RGBAWord <<= 8;
	RGBAWord |= G;
	RGBAWord <<= 8;
	RGBAWord |= R;

	return RGBAWord;
}

#include "spacecomp_post_cpp.h"
