#include <CL/cl.h>
#include <CL/cl_gl.h>

#include <stdio.h>
#include <stdlib.h>

#include "project_opencl.h"

Cclprog::Cclprog()
{
	device_id = NULL;
	context = NULL;
	command_queue = NULL;
	program = NULL;
	kernel = NULL;
	platform_id = NULL;
}

Cclprog::~Cclprog()
{
}


void print_ocl_error(int num)
{
	switch (num)
	{
        case CL_SUCCESS:							break;
        case CL_DEVICE_NOT_FOUND:					printf("Device not found.\n");	break;
        case CL_DEVICE_NOT_AVAILABLE:				printf("Device not available\n");	break;
        case CL_COMPILER_NOT_AVAILABLE:				printf("Compiler not available\n");	break;
        case CL_MEM_OBJECT_ALLOCATION_FAILURE:		printf("Memory object allocation failure\n");	break;
        case CL_OUT_OF_RESOURCES:					printf("Out of resources\n");	break;
        case CL_OUT_OF_HOST_MEMORY:					printf("Out of host memory\n");	break;
        case CL_PROFILING_INFO_NOT_AVAILABLE:		printf("Profiling information not available\n");	break;
        case CL_MEM_COPY_OVERLAP:					printf("Memory copy overlap\n");	break;
        case CL_IMAGE_FORMAT_MISMATCH:				printf("Image format mismatch\n");	break;
        case CL_IMAGE_FORMAT_NOT_SUPPORTED:			printf("Image format not supported\n");	break;
        case CL_BUILD_PROGRAM_FAILURE:				printf("Program build failure\n");	break;
        case CL_MAP_FAILURE:						printf("Map failure\n");	break;
        case CL_INVALID_VALUE:						printf("Invalid value\n");	break;
        case CL_INVALID_DEVICE_TYPE:				printf("Invalid device type\n");	break;
        case CL_INVALID_PLATFORM:					printf("Invalid platform\n");	break;
        case CL_INVALID_DEVICE:						printf("Invalid device\n");	break;
        case CL_INVALID_CONTEXT:					printf("Invalid context\n");	break;
        case CL_INVALID_QUEUE_PROPERTIES:			printf("Invalid queue properties\n");	break;
        case CL_INVALID_COMMAND_QUEUE:				printf("Invalid command queue\n");	break;
        case CL_INVALID_HOST_PTR:					printf("Invalid host pointer\n");	break;
        case CL_INVALID_MEM_OBJECT:					printf("Invalid memory object\n");	break;
        case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR:	printf("Invalid image format descriptor\n");	break;
        case CL_INVALID_IMAGE_SIZE:					printf("Invalid image size\n");	break;
        case CL_INVALID_SAMPLER:					printf("Invalid sampler\n");	break;
        case CL_INVALID_BINARY:						printf("Invalid binary\n");	break;
        case CL_INVALID_BUILD_OPTIONS:				printf("Invalid build options\n");	break;
        case CL_INVALID_PROGRAM:					printf("Invalid program\n");	break;
        case CL_INVALID_PROGRAM_EXECUTABLE:			printf("Invalid program executable\n");	break;
        case CL_INVALID_KERNEL_NAME:				printf("Invalid kernel name\n");	break;
        case CL_INVALID_KERNEL_DEFINITION:			printf("Invalid kernel definition\n");	break;
        case CL_INVALID_KERNEL:						printf("Invalid kernel\n");	break;
        case CL_INVALID_ARG_INDEX:					printf("Invalid argument index\n");	break;
        case CL_INVALID_ARG_VALUE:					printf("Invalid argument value\n");	break;
        case CL_INVALID_ARG_SIZE:					printf("Invalid argument size\n");	break;
        case CL_INVALID_KERNEL_ARGS:				printf("Invalid kernel arguments\n");	break;
        case CL_INVALID_WORK_DIMENSION:				printf("Invalid work dimension\n");	break;
        case CL_INVALID_WORK_GROUP_SIZE:			printf("Invalid work group size\n");	break;
        case CL_INVALID_WORK_ITEM_SIZE:				printf("Invalid work item size\n");	break;
        case CL_INVALID_GLOBAL_OFFSET:				printf("Invalid global offset\n");	break;
        case CL_INVALID_EVENT_WAIT_LIST:			printf("Invalid event wait list\n");	break;
        case CL_INVALID_EVENT:						printf("Invalid event\n");	break;
        case CL_INVALID_OPERATION:					printf("Invalid operation\n");	break;
        case CL_INVALID_GL_OBJECT:					printf("Invalid OpenGL object\n");	break;
        case CL_INVALID_BUFFER_SIZE:				printf("Invalid buffer size\n");	break;
        case CL_INVALID_MIP_LEVEL:					printf("Invalid mip-map level\n");	break;
        default:									printf("Unknown\n");	break;
    }
}

void Cclprog::print_error()
{
	print_ocl_error(err);
}


void Cclprog::init(char* fileName, char* kernelName)
{
	FILE *fp;
	size_t source_size;

	/* Load the source code containing the kernel*/
	fp = fopen(fileName, "r");
	if (!fp)
		fprintf(stderr, "Failed to load kernel.\n");
	else
		printf("Kernel is loaded.\n");

	source_str = (char*)malloc(MAX_SOURCE_SIZE);
	source_size = fread( source_str, 1, MAX_SOURCE_SIZE, fp);
	fclose( fp );

	/* Get Platform and Device Info */
	err = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
	if (err) print_error();
	err = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, &ret_num_devices);
	if (err) print_error();

	/* Create OpenCL context */

	//cl_context_properties props[] = {	CL_GL_CONTEXT_KHR, (cl_context_properties) wglGetCurrentContext(), 
	//									CL_WGL_HDC_KHR, (cl_context_properties) wglGetCurrentDC(), 
	//									CL_CONTEXT_PLATFORM, (cl_context_properties)platform_id, 0};
	//context = clCreateContext(props, 1, &device_id, NULL, NULL, &err);

	context = clCreateContext( NULL, 1, &device_id, NULL, NULL, &err);
	if (err) print_error();

	/* Create Command Queue */
	command_queue = clCreateCommandQueue(context, device_id, 0, &err);
	if (err) print_error();

	/* Create Kernel Program from the source */
	program = clCreateProgramWithSource(context, 1, (const char **)&source_str, (const size_t *)&source_size, &err);
	if (err) print_error();

	/* Build Kernel Program */
	err = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
	if (err)
	{
		size_t sz;
		char *errlog;

		clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, 0, &sz);

		errlog = (char*)malloc(sz + 1);
		clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sz, errlog, 0);

		fprintf(stderr, "failed to build program:\n%s\n", errlog);
	}

	/* Create OpenCL Kernel */
	kernel = clCreateKernel(program, kernelName, &err);
	if (err) print_error();
}


void Cclprog::deinit()
{
	/* Finalization */
	err = clFlush(command_queue);
	err = clFinish(command_queue);
	err = clReleaseKernel(kernel);
	err = clReleaseProgram(program);
	err = clReleaseCommandQueue(command_queue);
	err = clReleaseContext(context);

	free(source_str);
}
