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

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

#include "project_opencl.h"
#include "project_volumes.h"
#include "fx_colors.h"

Cclprog fxprg_plasma;

cl_mem memobj_volume_fx_plasma = NULL;
cl_mem memobj_fxp_sin1 = NULL;
cl_mem memobj_fxp_sin2 = NULL;
cl_mem memobj_fxp_sin3 = NULL;

cl_event fx_event;

unsigned char *volume_fx_plasma_data;

#define fxp_sine_size 32768

int fxp_fsin1[fxp_sine_size],fxp_fsin2[fxp_sine_size],fxp_fsin3[fxp_sine_size];
unsigned int fxp_argb[256];

void get_fx_plasma_cols(unsigned int *cols)
{
	for (int i=0; i<256; i++)
	{
		cols[i] = fxp_argb[i];
	}
}

void opencl_fx_plasma_init(Cvolume *vol)
{
	fxprg_plasma.init("./data/cl/fx_plasma.cl", "fx_plasma");

	int vol_size = vol->GetWidth() * vol->GetHeight() * vol->GetDepth();
	volume_fx_plasma_data = (unsigned char*)malloc(vol_size * sizeof(unsigned char));

	memobj_volume_fx_plasma = clCreateBuffer(fxprg_plasma.context, CL_MEM_READ_WRITE, vol_size * sizeof(unsigned char), NULL, &fxprg_plasma.err);


	memobj_fxp_sin1 = clCreateBuffer(fxprg_plasma.context, CL_MEM_READ_ONLY, fxp_sine_size * sizeof(unsigned int), NULL, &fxprg_plasma.err);
	memobj_fxp_sin2 = clCreateBuffer(fxprg_plasma.context, CL_MEM_READ_ONLY, fxp_sine_size * sizeof(unsigned int), NULL, &fxprg_plasma.err);
	memobj_fxp_sin3 = clCreateBuffer(fxprg_plasma.context, CL_MEM_READ_ONLY, fxp_sine_size * sizeof(unsigned int), NULL, &fxprg_plasma.err);

    float l = (float)vol->GetWidth() / 128.0f;
    for (int i=0; i<fxp_sine_size; i++)
	{
		fxp_fsin1[i]=(int)(sin(i/(l*15.0))*96.0+96.0);
		fxp_fsin2[i]=(int)(sin(i/(l*20.0))*112.0+112.0);
		fxp_fsin3[i]=(int)(sin(i/(l*35.0))*128.0+128.0);
	}

    clEnqueueWriteBuffer(fxprg_plasma.command_queue, memobj_fxp_sin1, CL_TRUE, 0, fxp_sine_size * sizeof(unsigned int), fxp_fsin1, 0, NULL, NULL);
    clEnqueueWriteBuffer(fxprg_plasma.command_queue, memobj_fxp_sin2, CL_TRUE, 0, fxp_sine_size * sizeof(unsigned int), fxp_fsin2, 0, NULL, NULL);
    clEnqueueWriteBuffer(fxprg_plasma.command_queue, memobj_fxp_sin3, CL_TRUE, 0, fxp_sine_size * sizeof(unsigned int), fxp_fsin3, 0, NULL, NULL);


//	SetPalette(fxp_argb, GetColor(0,0,0,0), GetColor(255,127,0,191), 0, 63);
//    SetPalette(fxp_argb, GetColor(255,127,0,192), GetColor(127,255,255,255), 64, 127);
//    SetPalette(fxp_argb, GetColor(127,255,255,255), GetColor(0,127,255,192), 128, 191);
//    SetPalette(fxp_argb, GetColor(0,127,255,191), GetColor(0,0,0,0), 192, 255);

    SetPalette(fxp_argb, GetColor(0,0,0,0), GetColor(255,255,255,15), 0, 127);
    SetPalette(fxp_argb, GetColor(255,255,255,15), GetColor(0,0,0,0), 128, 255);
}

//unsigned char* opencl_fx_plasma_run(int vol_width, int ptime)
cl_mem opencl_fx_plasma_run(int vol_width, int ptime)
{
	clSetKernelArg(fxprg_plasma.kernel, 0, sizeof(cl_mem), (void *)&memobj_volume_fx_plasma);
	clSetKernelArg(fxprg_plasma.kernel, 1, sizeof(cl_mem), (void *)&memobj_fxp_sin1);
	clSetKernelArg(fxprg_plasma.kernel, 2, sizeof(cl_mem), (void *)&memobj_fxp_sin2);
	clSetKernelArg(fxprg_plasma.kernel, 3, sizeof(cl_mem), (void *)&memobj_fxp_sin3);

	clSetKernelArg(fxprg_plasma.kernel, 4, sizeof(int), (void *)&vol_width);
	clSetKernelArg(fxprg_plasma.kernel, 5, sizeof(int), (void *)&ptime);

	/* Execute OpenCL Kernel */

	size_t global_work_size[] = {vol_width >> 2, vol_width, vol_width};
	size_t local_work_size[] = {4, 4, 8};
	clEnqueueNDRangeKernel(fxprg_plasma.command_queue, fxprg_plasma.kernel, 3, NULL, global_work_size, local_work_size, 0, NULL, &fx_event);

	clWaitForEvents(1, &fx_event);

	int vol_size = vol_width * vol_width * vol_width;
//	clEnqueueReadBuffer(fxprg_plasma.command_queue, memobj_volume_fx_plasma, CL_TRUE, 0, vol_size * sizeof(unsigned char), volume_fx_plasma_data, 0, NULL, NULL);

//	ClearBorders(vol_width, vol_width, vol_width, (unsigned char*)volume_fx_plasma_data);

//	return (unsigned char*)volume_fx_plasma_data;
	return memobj_volume_fx_plasma;
}

void opencl_fx_plasma_deinit()
{
	fxprg_plasma.deinit();

	clReleaseMemObject(memobj_volume_fx_plasma);
	clReleaseMemObject(memobj_fxp_sin1);
	clReleaseMemObject(memobj_fxp_sin2);
	clReleaseMemObject(memobj_fxp_sin3);
}
