/*
 * Raw_Processor_OpenCL.cpp
 *
 *  Created on: 19.10.2010
 *      Author: uwe
 */

#ifdef __OPENCL__
#include "Raw_Processor_OpenCL.h"

namespace MobilePhotoTool{




void  ERROR_clEnqueueNDRange1(cl_uint error1, MPT_Exception *exception)
{

	switch(error1){																											\
	case CL_SUCCESS:																										\
		break;																												\
	case CL_INVALID_PROGRAM_EXECUTABLE:																						\
		MPT_EXCEPTION(	"if there is no successfully built program"															\
						"executable available for device associated with command_queue.");									\
		break;																												\
	case CL_INVALID_COMMAND_QUEUE:																							\
		MPT_EXCEPTION(	"if command_queue is not a valid command-queue.");													\
		break;																												\
	case CL_INVALID_KERNEL:																									\
		MPT_EXCEPTION(	"if kernel is not a valid kernel object.");															\
		break;																												\
	case CL_INVALID_CONTEXT:																								\
		MPT_EXCEPTION(	"if context associated with command_queue and kernel are not"										\
						"the same or if the context associated with command_queue and events in event_wait_list"			\
						"are not the same.");																				\
		break;																												\
	case CL_INVALID_KERNEL_ARGS:																							\
		MPT_EXCEPTION(	"if the kernel argument values have not been specified.");											\
		break;																												\
	case CL_INVALID_WORK_DIMENSION: 																						\
		MPT_EXCEPTION(	"if work_dim is not a valid value (i.e. a value between"											\
						"1 and 3).");																						\
		break;																												\
	case CL_INVALID_GLOBAL_WORK_SIZE:																						\
		MPT_EXCEPTION(	"if global_work_size is NULL, or if any of the"														\
						"values specified in global_work_size[0], … global_work_size[work_dim – 1] are 0 or"				\
						"exceed the range given by the sizeof(size_t) for the device on which the kernel"					\
						"execution will be enqueued.");																		\
		break;																												\
	default:																												\
		MPT_EXCEPTION( "Some bad thing happened");																			\
		break;																												\
	};
}
#define ERROR_clEnqueueNDRange(error1)	ERROR_clEnqueueNDRange1(error1, exception)
	/*
	 * Starts the OpenCL stuff platform, device ...
	 */
	Raw_Processor_OpenCL::Raw_Processor_OpenCL(MPT_Exception *exception, IFD *raw_ifd, IFD *ifd0)
	: Raw_Processor(exception, raw_ifd,ifd0)
	{
		temp_image = new unsigned int[this->length * this->width];
	}

	/**
	 * init special CPU GPU stuff.
	 */
	void Raw_Processor_OpenCL::init(MPT_Exception *exception)
	{
	/*
		init(exception, raw_ifd, ifd0);
		MPT_EXCEPTION_TEST
	 */
		createBuffer(exception);
		MPT_EXCEPTION_TEST;

		initializeCL(exception);
		MPT_EXCEPTION_TEST;

		basic_pipline(exception);
		MPT_EXCEPTION_TEST;

		//cleanupCL();
	}

	void Raw_Processor_OpenCL::createBuffer(MPT_Exception *exception)
	{
		unsigned int 	pos, linestride, *numbits;
		size_t			number;

		this->start_pointer		= new unsigned int[this->number_components];
		this->linestride		= new unsigned int[this->number_components];
		this->pixel				= NULL;

		linestride = this->width + ((this->width % 16 == 0) ? 0 : (16 - (this->width % 16)));

		num_of_ushort16 = linestride / 16;

		for( pos = 0; pos < this->number_components; pos += 1){

			this->start_pointer[pos] = 0;
			this->linestride[pos] = linestride;
		}

		this->raw_linestride = 0;
		numbits = this->raw_ifd->getBitsPerSample(exception, &number);
		if(number > 0){
			if(numbits[0] == 12){
				if(this->width % 2 == 0){
					this->raw_linestride = (this->width + this->width / 2);
					raw_buffersize = this->length * (this->width + this->width / 2);
				}else{
					MPT_EXCEPTION("Unknown Format");
				}
			}else if(numbits[0] == 16){
				this->raw_linestride = this->width * 2;
				raw_buffersize = this->length * this->width * 2;
			}else{
				MPT_EXCEPTION("Unknown Format");
			}

			data = this->raw_ifd->getRawData(exception);

		}else{
			MPT_EXCEPTION("Unknown Format");
		}


	}



	std::string Raw_Processor_OpenCL::convertToString(const char *filename)
	{
		size_t size;
		char*  str;
		std::string s;

		std::fstream f(filename, (std::fstream::in | std::fstream::binary));

		if(f.is_open())
		{
			size_t fileSize;
			f.seekg(0, std::fstream::end);
			size = fileSize = f.tellg();
			f.seekg(0, std::fstream::beg);

			str = new char[size+1];
			if(!str)
			{
				f.close();
				return NULL;
			}

			f.read(str, fileSize);
			f.close();
			str[size] = '\0';

			s = str;
			delete[] str;
			return s;
		}
		else
		{
			std::cout << "\nFile containg the kernel code(\".cl\") not found. Please copy the required file in the folder containg the executable.\n";
			exit(1);
		}
		return NULL;
	}


	/*
	 * \brief OpenCL related initialization
	 *        Create Context, Device list, Command Queue
	 *        Create OpenCL memory buffer objects
	 *        Load CL file, compile, link CL source
	 *		  Build program and kernel objects
	 */
	void Raw_Processor_OpenCL::initializeCL(MPT_Exception *exception)
	{
		size_t deviceListSize;
		cl_uint numPlatforms;
		cl_int status = 0;


		/*
		 * Have a look at the available platforms and pick either
		 * the AMD one if available or a reasonable default.
		 */
		cl_platform_id platform = NULL;
		status = clGetPlatformIDs(0, NULL, &numPlatforms);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Getting Platforms. (clGetPlatformsIDs)\n");
		}

		if(numPlatforms > 0)
		{
			cl_platform_id* platforms = new cl_platform_id[numPlatforms];
			status = clGetPlatformIDs(numPlatforms, platforms, NULL);
			if(status != CL_SUCCESS)
			{
				MPT_EXCEPTION("Error: Getting Platform Ids. (clGetPlatformsIDs)\n");
			}
			for(unsigned int i=0; i < numPlatforms; ++i)
			{
				char pbuff[100];
				status = clGetPlatformInfo(
							platforms[i],
							CL_PLATFORM_VENDOR,
							sizeof(pbuff),
							pbuff,
							NULL);
				if(status != CL_SUCCESS)
				{
					MPT_EXCEPTION("Error: Getting Platform Info.(clGetPlatformInfo)\n");
				}
				platform = platforms[i];
				if(!strcmp(pbuff, "Advanced Micro Devices, Inc."))
				{
					break;
				}
			}
			delete platforms;
		}

		if(NULL == platform)
		{
			MPT_EXCEPTION("NULL platform found so Exiting Application." );
		}

		/*
		 * If we could find our platform, use it. Otherwise use just available platform.
		 */
		cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 };

		/////////////////////////////////////////////////////////////////
		// Create an OpenCL context
		/////////////////////////////////////////////////////////////////
		context = clCreateContextFromType(cps,
										  CL_DEVICE_TYPE_GPU,
										  NULL,
										  NULL,
										  &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Context. (clCreateContextFromType)\n");
		}

		/* First, get the size of device list data */
		status = clGetContextInfo(context,
								  CL_CONTEXT_DEVICES,
								  0,
								  NULL,
								  &deviceListSize);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION(
				"Error: Getting Context Info \
				(device list size, clGetContextInfo)\n");
		}

		/////////////////////////////////////////////////////////////////
		// Detect OpenCL devices
		/////////////////////////////////////////////////////////////////
		devices = (cl_device_id *)malloc(deviceListSize);
		if(devices == 0)
		{
			MPT_EXCEPTION("Error: No devices found.\n");
		}

		/* Now, get the device list data */
		status = clGetContextInfo(
					 context,
					 CL_CONTEXT_DEVICES,
					 deviceListSize,
					 devices,
					 NULL);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION(
				"Error: Getting Context Info \
				(device list, clGetContextInfo)\n");
		}

		/////////////////////////////////////////////////////////////////
		// Create an OpenCL command queue
		/////////////////////////////////////////////////////////////////
		commandQueue = clCreateCommandQueue(
						   context,
						   devices[0],
						   0,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Creating Command Queue. (clCreateCommandQueue)\n");
		}

		/////////////////////////////////////////////////////////////////
		// Create OpenCL memory buffers
		/////////////////////////////////////////////////////////////////


		inputBuffer = clCreateBuffer(
						  context,
						  CL_MEM_READ_ONLY,
						  sizeof(cl_uchar) * raw_buffersize,
						  NULL,
						  &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (inputBuffer)\n");
		}

		outputBuffer = clCreateBuffer(
						   context,
						   CL_MEM_WRITE_ONLY,
						   sizeof(cl_uchar) * width * length * 3,
						   NULL,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (outputBuffer)\n");
		}

		comp0 = clCreateBuffer(
						   context,
						   CL_MEM_READ_WRITE,
						   sizeof(cl_ushort) * this->linestride[0] * length,
						   NULL,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (outputBuffer)\n");
		}

		comp1 = clCreateBuffer(
						   context,
						   CL_MEM_READ_WRITE,
						   sizeof(cl_ushort) * this->linestride[0] * length,
						   NULL,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (outputBuffer)\n");
		}

		comp2 = clCreateBuffer(
						   context,
						   CL_MEM_READ_WRITE,
						   sizeof(cl_ushort) * this->linestride[0] * length,
						   NULL,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (outputBuffer)\n");
		}

		image_buffer = clCreateBuffer(	context,
										CL_MEM_READ_WRITE,
										sizeof(cl_uint) * this->length * this->width,
										NULL,
										&status);
		if(status != CL_SUCCESS){
			MPT_EXCEPTION("Error: clCreateBuffer(outputBuffer) \n");
		}

		gamma_curve_cl = clCreateBuffer(
						   context,
						   CL_MEM_READ_ONLY,
						   sizeof(cl_ushort) * 65536,
						   NULL,
						   &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: clCreateBuffer (outputBuffer)\n");
		}

		matrix_buffer_01 = clCreateBuffer(	context,
											CL_MEM_READ_ONLY,
											sizeof(cl_float) * 12,
											NULL,
											&status);
		if(status != CL_SUCCESS){
			MPT_EXCEPTION("Error: clCreateBuffer(matrix_buffer_01");
		}

		/////////////////////////////////////////////////////////////////
		// Load CL file, build CL program object, create CL kernel object
		/////////////////////////////////////////////////////////////////
		const char * filename  = "OpenCL_Kernel.cl";
		std::string  sourceStr = convertToString(filename);
		const char * source    = sourceStr.c_str();
		size_t sourceSize[]    = { strlen(source) };

		program = clCreateProgramWithSource(
					  context,
					  1,
					  &source,
					  sourceSize,
					  &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION(
				   "Error: Loading Binary into cl_program \
				   (clCreateProgramWithBinary)\n");
		}

		/* create a cl program executable for all the devices specified */
		status = clBuildProgram(program, 1, devices, "-g", NULL, NULL);
		char buf[0x10000];
		clGetProgramBuildInfo(	program,
								*devices,
								CL_PROGRAM_BUILD_LOG,
								0x10000,
								buf,
								NULL);
		std::cout<<buf <<endl;

		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Building Program (clBuildProgram)\n");
		}

		/* get a kernel object handle for a kernel with the given name */
		load_raw = clCreateKernel(program, "load_raw", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}

		/* get a kernel object handle for a kernel with the given name */
		demosaik_bilienar_comp0a = clCreateKernel(program, "demosaik_bilienar_comp0a", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}
		/* get a kernel object handle for a kernel with the given name */
		demosaik_bilienar_comp0b = clCreateKernel(program, "demosaik_bilienar_comp0b", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");;
		}		/* get a kernel object handle for a kernel with the given name */
		demosaik_bilienar_comp1 = clCreateKernel(program, "demosaik_bilienar_comp1", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}		/* get a kernel object handle for a kernel with the given name */
		demosaik_bilienar_comp2a = clCreateKernel(program, "demosaik_bilienar_comp2a", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}		/* get a kernel object handle for a kernel with the given name */
		demosaik_bilienar_comp2b = clCreateKernel(program, "demosaik_bilienar_comp2b", &status);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}
		gamma_correction = clCreateKernel(program, "gamma_correction", &status);
		if(status != CL_SUCCESS){
			MPT_EXCEPTION("Error: Creating Kernel from program. (clCreateKernel)\n");
		}
		write_to_imagebuffer = clCreateKernel(program, "write_to_imagebuffer", &status);
		if(status != CL_SUCCESS){
			MPT_EXCEPTION("Error: Creating Kernel from progamm. (clCreateKernel)\n");
		}

		matrix3x3_mul = clCreateKernel(program, "matrix3x3_mul", &status);
		if(status != CL_SUCCESS){
			MPT_EXCEPTION("Error: Creating Kernel from progamm. (clCreateKernel)\n");
		}

		status = clGetDeviceInfo(
			devices[0],
			CL_DEVICE_MAX_WORK_GROUP_SIZE,
			sizeof(size_t),
			(void*)&maxWorkGroupSize,
			NULL);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Getting Device Info. (clGetDeviceInfo)\n");
		}

		status = clGetDeviceInfo(
			devices[0],
			CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS,
			sizeof(cl_uint),
			(void*)&maxDims,
			NULL);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Getting Device Info. (clGetDeviceInfo)\n");
		}

		status = clGetDeviceInfo(
			devices[0],
			CL_DEVICE_MAX_WORK_ITEM_SIZES,
			sizeof(size_t)*maxDims,
			(void*)maxWorkItemSizes,
			NULL);
		if(status != CL_SUCCESS)
		{
			MPT_EXCEPTION("Error: Getting Device Info. (clGetDeviceInfo)\n");
		}

		/*
		globalThreads[0] = width;
		localThreads[0]  = 1;
		 */
	}

	int  Raw_Processor_OpenCL::cleanupCL(void)
	{
		cl_int status;

		status = clReleaseKernel(load_raw);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(demosaik_bilienar_comp0a);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(demosaik_bilienar_comp0b);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(demosaik_bilienar_comp1);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(demosaik_bilienar_comp2a);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(demosaik_bilienar_comp2b);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(gamma_correction);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(write_to_imagebuffer);
		if(status != CL_SUCCESS){
			std::cout<<"Error: In clReleaseKernel \n";
			return 1;
		}
		status = clReleaseKernel(matrix3x3_mul);
		if(status != CL_SUCCESS){
			std::cout<<"Erroro; In clReleaseKernel \n";
		}



		status = clReleaseProgram(program);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseProgram\n";
			return 1;
		}
		status = clReleaseMemObject(inputBuffer);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (inputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(outputBuffer);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (outputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(comp0);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (outputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(comp1);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (outputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(comp2);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (outputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(gamma_curve_cl);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseMemObject (outputBuffer)\n";
			return 1;
		}
		status = clReleaseMemObject(image_buffer);
		if(status != CL_SUCCESS){
			std::cout<<"Error; In clReleaseMemObject( image_buffer) \n";
			return 1;
		}
		status = clReleaseMemObject(matrix_buffer_01);
		if(status != CL_SUCCESS){
			std::cout<<"Error; in clReleaseMemObject(matrix_buffer_01 \n";
			return 1;
		}

		status = clReleaseCommandQueue(commandQueue);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseCommandQueue\n";
			return 1;
		}
		status = clReleaseContext(context);
		if(status != CL_SUCCESS)
		{
			std::cout<<"Error: In clReleaseContext\n";
			return 1;
		}

		return 0;
	}

	/**
	 *	Decode the raw Data and stores the components in the raw array.
	 */
	void Raw_Processor_OpenCL::loadRaw(MPT_Exception *exception, cl_command_queue *queue,cl_event *wait_ev, cl_event *fire_ev)
	{
		cl_int result;
		cl_event ev;
		size_t offset, cp;
		size_t global_work_size;
		cl_uint num_events_in_wait_list = 0;

		offset				= 0;
		cp 					= raw_buffersize;
		global_work_size	= (size_t)this->length;

		if(wait_ev != NULL){
			num_events_in_wait_list = 1;
		}

		result = clEnqueueWriteBuffer(	*queue,
										inputBuffer,
										CL_FALSE,
										offset,
										cp,
										data,
										num_events_in_wait_list,
										wait_ev,
										&ev);

		switch(result){
		case CL_SUCCESS:
			break;
		case CL_INVALID_COMMAND_QUEUE:
			MPT_EXCEPTION("CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue.");
			break;
		case CL_INVALID_CONTEXT:
			MPT_EXCEPTION("if the context associated with command_queue and buffer are not the same ");
			break;
		case CL_INVALID_MEM_OBJECT:
			MPT_EXCEPTION("if buffer is not a valid buffer object.");
		case CL_INVALID_VALUE:
			MPT_EXCEPTION(	"if the region being read or written specified by (offset, cb) is out of"
							"bounds or if ptr is a NULL value");
		case CL_INVALID_EVENT_WAIT_LIST:
			MPT_EXCEPTION(	"if event_wait_list is NULL and"
							"num_events_in_wait_list > 0, or event_wait_list is not NULL and"
							"num_events_in_wait_list is 0, or if event objects in event_wait_list are not valid events.");
		case CL_MISALIGNED_SUB_BUFFER_OFFSET:
			MPT_EXCEPTION(	"if buffer is a sub-buffer object and offset"
							"specified when the sub-buffer object is created is not aligned to"
							"CL_DEVICE_MEM_BASE_ADDR_ALIGN value for device associated with queue");
		case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST:
			MPT_EXCEPTION(	"if the read and write"
							"operations are blocking and the execution status of any of the events in event_wait_list is"
							"a negative integer value");
		case CL_MEM_OBJECT_ALLOCATION_FAILURE:
			MPT_EXCEPTION("if there is a failure to allocate memory for data store associated with buffer.");
		case CL_OUT_OF_RESOURCES:
			MPT_EXCEPTION(	"if there is a failure to allocate resources required by the"
							"OpenCL implementation on the device.");
		case CL_OUT_OF_HOST_MEMORY:
			MPT_EXCEPTION(	"if there is a failure to allocate resources required by the"
							"OpenCL implementation on the host.");
		default:
			MPT_EXCEPTION("Can't write to the memory");
		}

		clSetKernelArg(load_raw,0, sizeof(inputBuffer), (void*) &inputBuffer );
		clSetKernelArg(load_raw,1, sizeof(comp0), (void*) &comp0 );
		clSetKernelArg(load_raw,2, sizeof(comp1), (void*) &comp1 );
		clSetKernelArg(load_raw,3, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(load_raw,4, sizeof(this->raw_linestride), (void*) &(this->raw_linestride) );
		clSetKernelArg(load_raw,5, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(load_raw,6, sizeof(this->width), (void*) &(this->width));

		result = clEnqueueNDRangeKernel(	*queue,
											load_raw,
											1,
											NULL,
											&global_work_size,
											NULL,
											1,
											&ev,
											fire_ev);

		ERROR_clEnqueueNDRange(result);


	}

	/**
	 *
	 */
	void Raw_Processor_OpenCL::demosaik(MPT_Exception *exception, cl_command_queue *queue,cl_event *wait_ev, cl_event *fire_ev)
	{
		cl_event events[5];
		cl_int result[5];
		size_t offset;
		static size_t global_work_size;
		static size_t half_global_work_size;
		cl_uint num_wait_ev = 0;

		offset					= 0;
		global_work_size		= (size_t)this->length - 2;
		half_global_work_size	= global_work_size / 2;

		clSetKernelArg(demosaik_bilienar_comp1, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(demosaik_bilienar_comp1, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(demosaik_bilienar_comp1, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(demosaik_bilienar_comp1, 3, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(demosaik_bilienar_comp1, 4, sizeof(this->width), (void*) &(this->width));

		if(wait_ev != NULL){
			num_wait_ev = 1;
		}

		result[0] = clEnqueueNDRangeKernel(	*queue,
											demosaik_bilienar_comp1,
											1,
											NULL,
											&half_global_work_size,
											NULL,
											num_wait_ev,
											wait_ev,
											&(events[0]));
		ERROR_clEnqueueNDRange(result[0]);
		MPT_EXCEPTION_TEST;

		clSetKernelArg(demosaik_bilienar_comp2a, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(demosaik_bilienar_comp2a, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(demosaik_bilienar_comp2a, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(demosaik_bilienar_comp2a, 3, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(demosaik_bilienar_comp2a, 4, sizeof(this->width), (void*) &(this->width));

		result[1] = clEnqueueNDRangeKernel(	*queue,
											demosaik_bilienar_comp2a,
											1,
											NULL,
											&half_global_work_size,
											NULL,
											1,
											wait_ev,
											&(events[2]));
		ERROR_clEnqueueNDRange(result[1]);
		MPT_EXCEPTION_TEST;

		clSetKernelArg(demosaik_bilienar_comp2b, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(demosaik_bilienar_comp2b, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(demosaik_bilienar_comp2b, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(demosaik_bilienar_comp2b, 3, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(demosaik_bilienar_comp2b, 4, sizeof(this->width), (void*) &(this->width));

		result[2] = clEnqueueNDRangeKernel(	*queue,
											demosaik_bilienar_comp2b,
											1,
											NULL,
											&half_global_work_size ,
											NULL,
											1,
											&(events[2]),
											&(events[1]));
		ERROR_clEnqueueNDRange(result[2]);
		MPT_EXCEPTION_TEST;

		clSetKernelArg(demosaik_bilienar_comp0a, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(demosaik_bilienar_comp0a, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(demosaik_bilienar_comp0a, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(demosaik_bilienar_comp0a, 3, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(demosaik_bilienar_comp0a, 4, sizeof(this->width), (void*) &(this->width));

		result[3] = clEnqueueNDRangeKernel(	*queue,
											demosaik_bilienar_comp0a,
											1,
											NULL,
											&half_global_work_size,
											NULL,
											2,
											&(events[0]),
											&(events[3]));
		ERROR_clEnqueueNDRange(result[3]);
		MPT_EXCEPTION_TEST;

		clSetKernelArg(demosaik_bilienar_comp0b, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(demosaik_bilienar_comp0b, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(demosaik_bilienar_comp0b, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(demosaik_bilienar_comp0b, 3, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(demosaik_bilienar_comp0b, 4, sizeof(this->width), (void*) &(this->width));

		result[4] = clEnqueueNDRangeKernel(	*queue,
											demosaik_bilienar_comp0b,
											1,
											NULL,
											&half_global_work_size,
											NULL,
											1,
											&(events[3]),
											fire_ev);
		ERROR_clEnqueueNDRange(result[4]);
		MPT_EXCEPTION_TEST;

	}

	void Raw_Processor_OpenCL::gamma(MPT_Exception *exception, cl_command_queue *queue,cl_event *wait_ev, cl_event *fire_ev)
	{

		cl_event ev;
		cl_int 	result;
		size_t	size = sizeof(unsigned short) * (pixeltypeMAX + 1);
		size_t	global_work_size	= (size_t)this->length - 2;
		size_t	wait_event = 0;

		if(wait_ev != NULL){
			wait_event = 1;
		}

		this->setup_gammacurve(pixeltypeMAX);
		result = clEnqueueWriteBuffer(	*queue,
										this->gamma_curve_cl,
										CL_TRUE,
										0,
										size,
										this->gamme_curve,
										wait_event,
										wait_ev,
										&ev);
		switch(result){
		case CL_SUCCESS:
			break;
		case CL_INVALID_COMMAND_QUEUE:
			MPT_EXCEPTION("CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue.");
			break;
		case CL_INVALID_CONTEXT:
			MPT_EXCEPTION("if the context associated with command_queue and buffer are not the same ");
			break;
		default:
			MPT_EXCEPTION("Can't write to the memory");
		}

		clSetKernelArg(gamma_correction, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(gamma_correction, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(gamma_correction, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(gamma_correction, 3, sizeof(gamma_curve_cl), (void*) &gamma_curve_cl);
		clSetKernelArg(gamma_correction, 4, sizeof(this->linestride[0]), (void*) this->linestride);
		clSetKernelArg(gamma_correction, 5, sizeof(this->width), (void*) &(this->width));

		result = clEnqueueNDRangeKernel(	*queue,
											gamma_correction,
											1,
											NULL,
											&global_work_size ,
											NULL,
											1,
											&ev,
											fire_ev);
		ERROR_clEnqueueNDRange(result);

	}


	/**
	 *
	 */
	void Raw_Processor_OpenCL::basic_pipline(MPT_Exception *exception)
	{
		cl_event events[5];

		this->loadRaw(exception, &(this->commandQueue), NULL, &(events[0]));
		MPT_EXCEPTION_TEST;

		this->demosaik(exception, &(this->commandQueue), &events[0], &(events[1]));
		MPT_EXCEPTION_TEST;

		this->matrix3x3_mul_01(exception, &(this->commandQueue), &(events[1]), &(events[2]), this->CamtoRGB);
		MPT_EXCEPTION_TEST;

		this->gamma(exception, &(this->commandQueue), &(events[2]), &(events[3]));
		MPT_EXCEPTION_TEST;

		clWaitForEvents(1, &(events[3]));
	}

	/*
	void Raw_Processor_OpenCL::setup_temp_image(MPT_Exception *exception)
	{


	}
*/

	void Raw_Processor_OpenCL::set_zoom(MPT_Exception *, float zoom, unsigned int size[2])
	{
		int size_i, size_o, i_pos1, i_pos2, i_pos3, i_end1;
		float start1, stop1, zoomsize, rest;

		if(zoom != zoomfactor){
			if(zoom < 1.0f && zoom > 0.0f ){
				this->zoomsize[0] = this->width * zoom;
				this->zoomsize[1] = this->length * zoom;
				zoomfactor = zoom;
			}else{
				this->zoomsize[0] = this->width;
				this->zoomsize[1] = this->length;
				zoomfactor = 1.0f;
			}

			if(size != NULL){
				size[0] = this->zoomsize[0];
				size[1] = this->zoomsize[1];
			}

			if( width > length){
				size_i = width;
				size_o = this->zoomsize[0];
			}else{
				size_i = length;
				size_o = this->zoomsize[1];
			}
			if(this->zooms == NULL){
				this->zooms =  (zoomer*)malloc(sizeof(struct zoomer) * size_i * 2);
				this->zoomssize = size_i * 2;
			}
			zoomfactor 	= (float)size_o / (float)size_i;
			zoomsize	= (float)size_i / (float)size_o;
			i_end1		= size_i;
			for(i_pos1 = 0, i_pos3 = 0; i_pos1 < i_end1; i_pos1++){
				i_pos2	= (float)i_pos1 / zoomsize;

				start1	= i_pos1 * zoomfactor;
				stop1	= (i_pos1 + 1) * zoomfactor;

				if(stop1 <= (i_pos2  + 1)){
					this->zooms[i_pos3].from	= i_pos1;
					this->zooms[i_pos3].to		= i_pos2;
					this->zooms[i_pos3].factor	= zoomfactor;
					i_pos3 += 1;
				}else{
					rest = 1.0f - (start1 - i_pos2);
					this->zooms[i_pos3].from	= i_pos1;
					this->zooms[i_pos3].to		= i_pos2;
					this->zooms[i_pos3].factor	= rest; //rest * zoomfactor;
					i_pos3 += 1;

					rest = zoomfactor - rest;
					this->zooms[i_pos3].from	= i_pos1;
					this->zooms[i_pos3].to		= i_pos2 + 1;
					this->zooms[i_pos3].factor	= rest; //rest * zoomfactor;
					i_pos3 += 1;
				}
			}
		}
	}

	/**
	 *
	 */
	void Raw_Processor_OpenCL::get_image(	MPT_Exception *exception,
												cl_command_queue *queue,
												cl_event *wait_ev,
												cl_event *fire_ev)
	{
		cl_int 	result;
		static size_t	global_work_size	= (size_t)this->length;
		cl_event event1;
		size_t	cp = this->length * this->width * 4;
		size_t	wait_events = 0;

		clSetKernelArg(write_to_imagebuffer, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(write_to_imagebuffer, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(write_to_imagebuffer, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(write_to_imagebuffer, 3, sizeof(image_buffer), (void*) &image_buffer);
		clSetKernelArg(write_to_imagebuffer, 4, sizeof(this->width), (void*) &(this->width));
		clSetKernelArg(write_to_imagebuffer, 5, sizeof(this->linestride[0]), (void*) &(this->linestride[0]));
		clSetKernelArg(write_to_imagebuffer, 6, sizeof(this->width), (void*) &(this->width));

		if(wait_ev != NULL){
			wait_events = 1;
		}

		result = clEnqueueNDRangeKernel(	*queue,
											write_to_imagebuffer,
											1,
											NULL,
											&global_work_size,
											NULL,
											wait_events,
											wait_ev,
											&event1);

		ERROR_clEnqueueNDRange(result);

		result = clEnqueueReadBuffer(	*queue,
										image_buffer,
										CL_FALSE,
										0,
										cp,
										this->temp_image,
										1,
										&event1,
										fire_ev);


	}

	void Raw_Processor_OpenCL::get_zoom_image(	MPT_Exception *exception,
													unsigned int *data_out,
													unsigned int linestride,
													unsigned int width,
													unsigned int length,
													unsigned int out_x0,
													unsigned int out_y0,
													unsigned int in_x0,
													unsigned int in_y0)
	{
		cl_event event1;
		unsigned int pos_iny, pos_outy, pos_iny_end, pos_inx_end, *in_image, *in, *out, pos_inx;

		pos_iny_end = in_y0 + length;
		pos_inx_end = in_x0 + width;



		if(		pos_iny_end	> this->zoomsize[1] &&
				pos_inx_end	> this->zoomsize[0]){
			pos_iny_end = this->zoomsize[1];
			pos_inx_end	= this->zoomsize[0];

		}
		get_image(	exception,
					&(this->commandQueue),
					NULL,
					&(event1));
		in_image = this->temp_image;

		clWaitForEvents(1, &event1);

		for(pos_iny = in_y0, pos_outy = out_y0; pos_iny < pos_iny_end; pos_iny += 1, pos_outy += 1){
			in	= &(this->temp_image[pos_iny * this->width + in_x0]);
			out	= data_out + pos_outy * linestride + out_x0;

			for(pos_inx = in_x0; pos_inx < pos_inx_end; pos_inx += 1){
				*out = *in;
				out += 1;
				in  += 1;

			}

		}
	}

	void Raw_Processor_OpenCL::matrix3x3_mul_01(	MPT_Exception *exception,
														cl_command_queue *queue,
														cl_event *wait_ev,
														cl_event *fire_ev,
														float *in_matrix)
	{

		cl_int result;
		cl_event ev;
		size_t offset, cp;
		size_t global_work_size;
		cl_uint num_events_in_wait_list = 0;
		int i;

		static float matrix[12];

		offset				= 0;
		cp 					= sizeof(cl_float) * 12;
		global_work_size	= (size_t)this->length;

		if(wait_ev != NULL){
			num_events_in_wait_list = 1;
		}

		for( i = 0; i < 9; i += 1){
			matrix[i] = in_matrix[i];
		}

		for( i = 9; i < 12; i += 1){
			matrix[i] = this->factor[i - 9] / this->factor[1];
		}

		result = clEnqueueWriteBuffer(	*queue,
										matrix_buffer_01,
										CL_FALSE,
										offset,
										cp,
										matrix,
										num_events_in_wait_list,
										wait_ev,
										&ev);

		switch(result){
		case CL_SUCCESS:
			break;
		case CL_INVALID_COMMAND_QUEUE:
			MPT_EXCEPTION("CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue.");
			break;
		case CL_INVALID_CONTEXT:
			MPT_EXCEPTION("if the context associated with command_queue and buffer are not the same ");
			break;
		case CL_INVALID_MEM_OBJECT:
			MPT_EXCEPTION("if buffer is not a valid buffer object.");
		case CL_INVALID_VALUE:
			MPT_EXCEPTION(	"if the region being read or written specified by (offset, cb) is out of"
							"bounds or if ptr is a NULL value");
		case CL_INVALID_EVENT_WAIT_LIST:
			MPT_EXCEPTION(	"if event_wait_list is NULL and"
							"num_events_in_wait_list > 0, or event_wait_list is not NULL and"
							"num_events_in_wait_list is 0, or if event objects in event_wait_list are not valid events.");
		case CL_MISALIGNED_SUB_BUFFER_OFFSET:
			MPT_EXCEPTION(	"if buffer is a sub-buffer object and offset"
							"specified when the sub-buffer object is created is not aligned to"
							"CL_DEVICE_MEM_BASE_ADDR_ALIGN value for device associated with queue");
		case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST:
			MPT_EXCEPTION(	"if the read and write"
							"operations are blocking and the execution status of any of the events in event_wait_list is"
							"a negative integer value");
		case CL_MEM_OBJECT_ALLOCATION_FAILURE:
			MPT_EXCEPTION("if there is a failure to allocate memory for data store associated with buffer.");
		case CL_OUT_OF_RESOURCES:
			MPT_EXCEPTION(	"if there is a failure to allocate resources required by the"
							"OpenCL implementation on the device.");
		case CL_OUT_OF_HOST_MEMORY:
			MPT_EXCEPTION(	"if there is a failure to allocate resources required by the"
							"OpenCL implementation on the host.");
		default:
			MPT_EXCEPTION("Can't write to the memory");
		}


		clSetKernelArg(matrix3x3_mul, 0, sizeof(comp0), (void*) &comp0);
		clSetKernelArg(matrix3x3_mul, 1, sizeof(comp1), (void*) &comp1);
		clSetKernelArg(matrix3x3_mul, 2, sizeof(comp2), (void*) &comp2);
		clSetKernelArg(matrix3x3_mul, 3, sizeof(matrix_buffer_01), (void*) &matrix_buffer_01);
		clSetKernelArg(matrix3x3_mul, 4, sizeof(this->width), (void*) &(this->width));
		clSetKernelArg(matrix3x3_mul, 5, sizeof(this->linestride[0]), (void*) &(this->linestride[0]));
		clSetKernelArg(matrix3x3_mul, 6, sizeof(this->width), (void*) &(this->width));

		result = clEnqueueNDRangeKernel(	*queue,
											matrix3x3_mul,
											1,
											NULL,
											&global_work_size,
											NULL,
											1,
											&ev,
											fire_ev);

		ERROR_clEnqueueNDRange(result);
	}

}
#endif
