// Difusion.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
/*CUDA*/
#include <cuda_runtime.h>

/*OpenCV*/
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "Diffusion.h"

//División techo.
int iDivUp(int a, int b){
    return ((a % b) != 0) ? (a / b + 1) : (a / b);
}
//PARAMETERS
#define BLOCKDIM_X 16
#define BLOCKDIM_Y 16
int MaxIter=100;
int lambda=1;
float lambdaf=(float)lambda/10.0f;
float denf=1+lambdaf*4;
void Change_den(int,void*){
	lambdaf=(float)lambda/10.0f;
	denf=1+lambdaf*4;
}

int main(int argc, char **argv){
	cudaSetDevice(0);
	/*Host Variables*/
	int imageW, imageH;		  //Size of Image
	cv::Mat frame_Original;   //Original Image
	double total_time;
	char time_ms[100];

	/*Device Variables*/
	uchar3 *Imagef0_dev;
	uchar3 *Imagef1_dev;
	uchar3 *ImageAux_dev;

	cv::namedWindow("Original Frame",CV_WINDOW_AUTOSIZE);
	cv::namedWindow("Difused Frame",CV_WINDOW_AUTOSIZE);


	cv::createTrackbar("MaxIter", "Difused Frame",&MaxIter,500,NULL,0);
	cv::createTrackbar("lambda", "Difused Frame",&lambda,50,Change_den,0);

	char name_imag[500];
	/*Load Image*/
	sprintf(name_imag,"../../Images/lena.jpg");
	frame_Original = cv::imread(name_imag,1);

	/*Size of Image*/
	imageW=frame_Original.cols;
	imageH=frame_Original.rows;
	size_t size=imageW*imageH*sizeof(uchar3);
	
	
	/*Create device memory*/
	cudaMalloc((void **)&Imagef0_dev,size);
	cudaMalloc((void **)&Imagef1_dev,size);
	cudaMalloc((void **)&ImageAux_dev,size);

	/*Copy Memory (Host-->Device)*/
	cudaMemcpy(ImageAux_dev,frame_Original.data,size,cudaMemcpyHostToDevice);
	
	/*Define the size of the grid and thread blocks*/
	dim3 threads(BLOCKDIM_X, BLOCKDIM_Y,1);
	dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y),1);

	cv::imshow("Original Frame",frame_Original);
	while(1){
		total_time = (double)cv::getTickCount();
		cudaMemcpy(Imagef0_dev,ImageAux_dev,size,cudaMemcpyDeviceToDevice);		
		/*Launch the Kernel Function*/
		for(int iter=0;iter<MaxIter;iter++){
			CUDA_Difusion(Imagef1_dev,Imagef0_dev,lambdaf,denf,imageW,imageH,grid,threads);	
			//Update f(x)
			/*Copy Memory (Device-->Device)*/
			cudaMemcpy(Imagef0_dev,Imagef1_dev,size,cudaMemcpyDeviceToDevice);		
		}

		/*Copy Memory (Device-->Host)*/
		cudaMemcpy(frame_Original.data,Imagef0_dev,size,cudaMemcpyDeviceToHost);

		total_time=(double)cv::getTickCount()-total_time;

		sprintf(time_ms,"Total time: %.2lf ms",(total_time/cv::getTickFrequency())*1000);
		cv::putText(frame_Original,time_ms,cv::Point(5,30),CV_FONT_HERSHEY_PLAIN,2.0,CV_RGB(255,0,0),2,8);
		
		cv::imshow("Difused Frame",frame_Original);
		
		char key=cv::waitKey(2);
		if(key==27)
			break;	
	}

	/*Clean Memory*/
	/*Host*/
	cv::destroyAllWindows();
	/*Device*/
	cudaFree(Imagef0_dev);
	cudaFree(Imagef1_dev);
	cudaFree(ImageAux_dev);

	return(0);
}

