#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <CL/cl.h>


static char *
load_program_source(const char *filename)
{
    struct stat statbuf;
    FILE          *fh;
    char          *source;
    fh = fopen(filename, "r");
    if (fh == 0)
         return 0;
    stat(filename, &statbuf);
    source = (char *) malloc(statbuf.st_size + 1);
    fread(source, statbuf.st_size, 1, fh);
    source[statbuf.st_size] = '\0';
    return source;
}




int main(int argc, const char** argv)
{

	char                  Buffer[1024];
	char                  Temp[2];
	cl_uint               ciDeviceCount = 1;
	cl_device_id          *devices;
	unsigned int          i;
	cl_device_id          *device_id;	
	cl_command_queue      commands;	
	cl_context            GPUcontext;
	const char*           filename = "my_kernel.cl";
	char                  *source;
	cl_mem                input;
	cl_mem                output;
	float                 *float_data = (float*)malloc(10 * sizeof(float));
	cl_platform_id        *context_platform;

	for (i = 0; i < 10; i++)
	{
		float_data[i] = ((float) rand() / (float) RAND_MAX);
	}



	cl_platform_id clSelectedPlatformID = NULL;

	cl_int ciErrNum = clGetPlatformIDs (ciDeviceCount, &clSelectedPlatformID, &ciDeviceCount);


	ciErrNum = clGetPlatformInfo (clSelectedPlatformID, CL_PLATFORM_NAME, sizeof(Buffer), Buffer, NULL);
	if (ciErrNum == CL_SUCCESS)
	{
		printf("CL_PLATFORM_NAME: %s\n", Buffer);
	}else{
		printf("clGetPlatformInfo Failed! (return code %i)\n", ciErrNum);
		exit(1);
	}

	ciErrNum = clGetPlatformInfo (clSelectedPlatformID, CL_PLATFORM_VERSION, sizeof(Buffer), Buffer, NULL);
	if (ciErrNum == CL_SUCCESS)
	{
		printf("CL_PLATFORM_VERSION: %s\n", Buffer);
	}else{
		printf("clGetPlatformInfo Failed! (return code %i)\n", ciErrNum);
		exit(1);
	}
	
	ciErrNum = clGetDeviceIDs (clSelectedPlatformID, CL_DEVICE_TYPE_ALL, 0, NULL, &ciDeviceCount);
//devices = (cl_device_id*)malloc(sizeof(cl_device_id) * 5);
	//ciErrNum = clGetDeviceIDs (NULL, CL_DEVICE_TYPE_ALL, 0, &devices, NULL);
	if (ciDeviceCount == 0)
	{
		printf("No devices found supporting OpenCL (return code %i)\n\n", ciErrNum);
		exit(1);
	}else if (ciErrNum != CL_SUCCESS){
		 printf("Error %i in clGetDeviceIDs call !!!\n\n", ciErrNum);
	}else{

printf("ciDeviceCount: %x\n", ciDeviceCount);
		if ((devices = (cl_device_id*)malloc(sizeof(cl_device_id) * ciDeviceCount)) != NULL)
		{
		//if(1==1) {
			ciErrNum = clGetDeviceIDs (NULL, CL_DEVICE_TYPE_ALL, ciDeviceCount, devices, &ciDeviceCount);
			if (ciErrNum == CL_SUCCESS)
			{
				for(i = 0; i < ciDeviceCount; ++i )
				{
                                //for(i = 0; i < 5; ++i )
                                //{

					clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(Buffer), &Buffer, NULL);
					printf("Device %d: %s\n", i, Buffer);

				}
			}else{
				printf("clGetDeviceInfo Failed! (return code %i)\n", ciErrNum);
				exit(1);
			}
		}else{
			printf("Failed to allocate memory!\n");
			exit(1);
		}
	}





	// Connect to a GPU compute device
	ciErrNum = clGetDeviceIDs(clSelectedPlatformID, CL_DEVICE_TYPE_ALL, ciDeviceCount, devices, NULL);
	if (ciErrNum != CL_SUCCESS){
		printf("Failed to connect to gpu compute device!\n");
		exit(1);
	}
printf("connected to gpu compute device\n");


/*
    int gpu = 1;
    ciErrNum = clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU, 1, &device_id, NULL);
    if (ciErrNum != CL_SUCCESS)
    {
        printf("Error: Failed to create a device group!\n");
        return EXIT_FAILURE;
    }
printf("connected to gpu compute device\n" );


*/
	// Create a compute context
	//context = clCreateContext(0, &device_id);
	GPUcontext = clCreateContext(NULL, ciDeviceCount, &devices[0], NULL, NULL, &ciErrNum);
	if(!GPUcontext)
	{
         	printf("Error: Failed to create a compute context! %d\n", ciErrNum);
         	exit(1);
	}



/*
	GPUcontext = clCreateContextFromType(NULL, CL_DEVICE_TYPE_ALL, NULL, NULL, &ciErrNum);
printf("errcode: %d\n", ciErrNum);

        if(ciErrNum != CL_SUCCESS)
        {
                printf("Error: Failed to create a compute context!\n");
                exit(1);
        }
*/

printf("created compute context\n" );
	// Create a command commands
	commands = clCreateCommandQueue(GPUcontext, devices[0], CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &ciErrNum);
//printf("done!\n");
	if(!commands)
	{
		
        	printf("Error: Failed to create a command commands! error: %i\n", ciErrNum);
        	exit(1);
	}

printf("created command queue done!\n");

/*
	printf("Loading program '%s'...\n", filename);

	source = load_program_source(filename);
	if(!source)
	{
    		printf("Error: Failed to load compute program from file!\n");
		exit(1);
	}


	input = clCreateBuffer(GPUcontext, CL_MEM_READ_WRITE, sizeof(float) * 10, NULL, NULL);
	if(!input)
	{
		printf("Error: Failed to allocate input data buffer on device!\n");
		exit(1);
	}

	ciErrNum = clEnqueueWriteBuffer(commands, input, CL_TRUE, 0, sizeof(float) * 10, (void *)float_data, 0, NULL, NULL);
	if (ciErrNum != CL_SUCCESS)
	{
		printf("Error: Failed to write to input data buffer on device!\n");
		exit(1);
	}

	output = clCreateBuffer(GPUcontext, CL_MEM_READ_WRITE, sizeof(float) * 10, NULL, NULL);
	if (!output)
	{
		printf("Error: Failed to allocate result buffer on device!\n");
		exit(1);
	}

	cl_program *programs =  (cl_program*)malloc(sizeof(cl_program));
	memset(programs, 0, sizeof(cl_program));
	cl_kernel *kernels = (cl_kernel*)malloc(sizeof(cl_kernel));
	memset(kernels, 0, sizeof(cl_kernel));

	programs[i] = clCreateProgramWithSource(GPUcontext, 1,(const char **) & source, NULL, &ciErrNum);
	if (!programs[i] || ciErrNum != CL_SUCCESS)
	{
		printf("Error: Failed to create compute program!\n");
		exit(1);
	}


	ciErrNum = clBuildProgram(programs[i], 0, NULL, NULL, NULL, NULL);
        if (ciErrNum != CL_SUCCESS)
        {       
		printf("Error: Failed to build program executable!\n");
                exit(1);
        }       

	kernels[i] = clCreateKernel(programs[i], "reduce", &ciErrNum);
	if (!kernels[i] || ciErrNum != CL_SUCCESS)
	{ 
		printf("Error: Failed to create compute kernel!\n");
		exit(1);
	}
*/

	printf("done!\n");




	return(0);
}
