local Array = require("Array")

local cl = require("opencl")
local CL = cl
local format = string.format

function print_aligned(k, v, n)
	n = n or 22
	local str = k..":"
	print(str..string.rep(" ", n-str:len())..tostring(v))
end

function print_array(array)
	for i=0, array.dim[1]-1 do
	for j=0, array.dim[2]-1 do
		print(format("(%d, %d) = %s", i, j, table.concat(array:getcell(i, j), " ")))
	end
	end
end

local platform_info = {
	CL.PLATFORM_VENDOR,
	CL.PLATFORM_NAME,
	CL.PLATFORM_VERSION,
	CL.PLATFORM_PROFILE,
	CL.PLATFORM_EXTENSIONS,
	
	names = {
		[CL.PLATFORM_VENDOR] = "PLATFORM_VENDOR",
		[CL.PLATFORM_NAME] = "PLATFORM_NAME",
		[CL.PLATFORM_VERSION] = "PLATFORM_VERSION",
		[CL.PLATFORM_PROFILE] = "PLATFORM_PROFILE",
		[CL.PLATFORM_EXTENSIONS] = "PLATFORM_EXTENSIONS",
	}
}

local device_info = {
	CL.DEVICE_ADDRESS_BITS,
	CL.DEVICE_AVAILABLE,
	CL.DEVICE_COMPILER_AVAILABLE,
	CL.DEVICE_DOUBLE_FP_CONFIG,
	CL.DEVICE_ENDIAN_LITTLE,
	CL.DEVICE_ERROR_CORRECTION_SUPPORT,
	CL.DEVICE_EXECUTION_CAPABILITIES,
	CL.DEVICE_EXTENSIONS,
	CL.DEVICE_GLOBAL_MEM_CACHE_SIZE,
	CL.DEVICE_GLOBAL_MEM_CACHE_TYPE,
	CL.DEVICE_GLOBAL_MEM_CACHELINE_SIZE,
	CL.DEVICE_GLOBAL_MEM_SIZE,
	CL.DEVICE_IMAGE_SUPPORT,
	CL.DEVICE_IMAGE2D_MAX_HEIGHT,
	CL.DEVICE_IMAGE2D_MAX_WIDTH,
	CL.DEVICE_IMAGE3D_MAX_DEPTH,
	CL.DEVICE_IMAGE3D_MAX_HEIGHT,
	CL.DEVICE_IMAGE3D_MAX_WIDTH,
	CL.DEVICE_LOCAL_MEM_SIZE,
	CL.DEVICE_LOCAL_MEM_TYPE,
	CL.DEVICE_MAX_CLOCK_FREQUENCY,
	CL.DEVICE_MAX_COMPUTE_UNITS,
	CL.DEVICE_MAX_CONSTANT_ARGS,
	CL.DEVICE_MAX_CONSTANT_BUFFER_SIZE,
	CL.DEVICE_MAX_MEM_ALLOC_SIZE,
	CL.DEVICE_MAX_PARAMETER_SIZE,
	CL.DEVICE_MAX_READ_IMAGE_ARGS,
	CL.DEVICE_MAX_SAMPLERS,
	CL.DEVICE_MAX_WORK_GROUP_SIZE,
	CL.DEVICE_MAX_WORK_ITEM_DIMENSIONS,
	CL.DEVICE_MAX_WORK_ITEM_SIZES,
	CL.DEVICE_MAX_WRITE_IMAGE_ARGS,
	CL.DEVICE_MEM_BASE_ADDR_ALIGN,
	CL.DEVICE_MIN_DATA_TYPE_ALIGN_SIZE,
	CL.DEVICE_NAME,
	CL.DEVICE_PLATFORM,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_CHAR ,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_SHORT,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_INT ,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_LONG ,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT ,
	CL.DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE,
	CL.DEVICE_PROFILE,
	CL.DEVICE_PROFILING_TIMER_RESOLUTION,
	CL.DEVICE_QUEUE_PROPERTIES,
	CL.DEVICE_SINGLE_FP_CONFIG,
	CL.DEVICE_TYPE,
	CL.DEVICE_VENDOR,
	CL.DEVICE_VERSION,
	CL.DRIVER_VERSION,
	
	names = {
		[CL.DEVICE_ADDRESS_BITS] = "DEVICE_ADDRESS_BITS",
		[CL.DEVICE_AVAILABLE] = "DEVICE_AVAILABLE",
		[CL.DEVICE_COMPILER_AVAILABLE] = "DEVICE_COMPILER_AVAILABLE",
		[CL.DEVICE_DOUBLE_FP_CONFIG] = "DEVICE_DOUBLE_FP_CONFIG",
		[CL.DEVICE_ENDIAN_LITTLE] = "DEVICE_ENDIAN_LITTLE",
		[CL.DEVICE_ERROR_CORRECTION_SUPPORT] = "DEVICE_ERROR_CORRECTION_SUPPORT",
		[CL.DEVICE_EXECUTION_CAPABILITIES] = "DEVICE_EXECUTION_CAPABILITIES",
		[CL.DEVICE_EXTENSIONS] = "DEVICE_EXTENSIONS",
		[CL.DEVICE_GLOBAL_MEM_CACHE_SIZE] = "DEVICE_GLOBAL_MEM_CACHE_SIZE",
		[CL.DEVICE_GLOBAL_MEM_CACHE_TYPE] = "DEVICE_GLOBAL_MEM_CACHE_TYPE",
		[CL.DEVICE_GLOBAL_MEM_CACHELINE_SIZE] = "DEVICE_GLOBAL_MEM_CACHELINE_SIZE",
		[CL.DEVICE_GLOBAL_MEM_SIZE] = "DEVICE_GLOBAL_MEM_SIZE",
		[CL.DEVICE_IMAGE_SUPPORT] = "DEVICE_IMAGE_SUPPORT",
		[CL.DEVICE_IMAGE2D_MAX_HEIGHT] = "DEVICE_IMAGE2D_MAX_HEIGHT",
		[CL.DEVICE_IMAGE2D_MAX_WIDTH] = "DEVICE_IMAGE2D_MAX_WIDTH",
		[CL.DEVICE_IMAGE3D_MAX_DEPTH] = "DEVICE_IMAGE3D_MAX_DEPTH",
		[CL.DEVICE_IMAGE3D_MAX_HEIGHT] = "DEVICE_IMAGE3D_MAX_HEIGHT",
		[CL.DEVICE_IMAGE3D_MAX_WIDTH] = "DEVICE_IMAGE3D_MAX_WIDTH",
		[CL.DEVICE_LOCAL_MEM_SIZE] = "DEVICE_LOCAL_MEM_SIZE",
		[CL.DEVICE_LOCAL_MEM_TYPE] = "DEVICE_LOCAL_MEM_TYPE",
		[CL.DEVICE_MAX_CLOCK_FREQUENCY] = "DEVICE_MAX_CLOCK_FREQUENCY",
		[CL.DEVICE_MAX_COMPUTE_UNITS] = "DEVICE_MAX_COMPUTE_UNITS",
		[CL.DEVICE_MAX_CONSTANT_ARGS] = "DEVICE_MAX_CONSTANT_ARGS",
		[CL.DEVICE_MAX_CONSTANT_BUFFER_SIZE] = "DEVICE_MAX_CONSTANT_BUFFER_SIZE",
		[CL.DEVICE_MAX_MEM_ALLOC_SIZE] = "DEVICE_MAX_MEM_ALLOC_SIZE",
		[CL.DEVICE_MAX_PARAMETER_SIZE] = "DEVICE_MAX_PARAMETER_SIZE",
		[CL.DEVICE_MAX_READ_IMAGE_ARGS] = "DEVICE_MAX_READ_IMAGE_ARGS",
		[CL.DEVICE_MAX_SAMPLERS] = "DEVICE_MAX_SAMPLERS",
		[CL.DEVICE_MAX_WORK_GROUP_SIZE] = "DEVICE_MAX_WORK_GROUP_SIZE",
		[CL.DEVICE_MAX_WORK_ITEM_DIMENSIONS] = "DEVICE_MAX_WORK_ITEM_DIMENSIONS",
		[CL.DEVICE_MAX_WORK_ITEM_SIZES] = "DEVICE_MAX_WORK_ITEM_SIZES",
		[CL.DEVICE_MAX_WRITE_IMAGE_ARGS] = "DEVICE_MAX_WRITE_IMAGE_ARGS",
		[CL.DEVICE_MEM_BASE_ADDR_ALIGN] = "DEVICE_MEM_BASE_ADDR_ALIGN",
		[CL.DEVICE_MIN_DATA_TYPE_ALIGN_SIZE] = "DEVICE_MIN_DATA_TYPE_ALIGN_SIZE",
		[CL.DEVICE_NAME] = "DEVICE_NAME",
		[CL.DEVICE_PLATFORM] = "DEVICE_PLATFORM",
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_CHAR] = "DEVICE_PREFERRED_VECTOR_WIDTH_CHAR" ,
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_SHORT] = "DEVICE_PREFERRED_VECTOR_WIDTH_SHORT",
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_INT] = "DEVICE_PREFERRED_VECTOR_WIDTH_INT" ,
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_LONG] = "DEVICE_PREFERRED_VECTOR_WIDTH_LONG" ,
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT] = "DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT" ,
		[CL.DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE] = "DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE",
		[CL.DEVICE_PROFILE] = "DEVICE_PROFILE",
		[CL.DEVICE_PROFILING_TIMER_RESOLUTION] = "DEVICE_PROFILING_TIMER_RESOLUTION",
		[CL.DEVICE_QUEUE_PROPERTIES] = "DEVICE_QUEUE_PROPERTIES",
		[CL.DEVICE_SINGLE_FP_CONFIG] = "DEVICE_SINGLE_FP_CONFIG",
		[CL.DEVICE_TYPE] = "DEVICE_TYPE",
		[CL.DEVICE_VENDOR] = "DEVICE_VENDOR",
		[CL.DEVICE_VERSION] = "DEVICE_VERSION",
		[CL.DRIVER_VERSION] = "DRIVER_VERSION",
	}
}


for i, plat in ipairs(cl.platforms()) do
	print("platform: ", i, plat)
	for i, v in ipairs(platform_info) do
		local k = platform_info.names[v]
		print_aligned(k, cl.platform_info(plat, v))
	end
	
	for i, dev in ipairs(cl.devices(plat)) do
		print("--------------------------------------")
		print("device: ", i, dev)
		
		for i, v in ipairs(device_info) do
			local k = device_info.names[v]
		--	print_aligned(k, cl.device_info(dev, v))
		end
	end
end

print("--------------------------------------")
print("gpu_device: ", cl.gpu_device())
print("cpu_device: ", cl.cpu_device())

local code = [[
__kernel void square(
	__global float* input,
	__global float* output
) {
	int i = get_global_id(0);
	int j = get_global_id(1);
	int w = get_global_size(0);

	output[i+j*w] = input[i+j*w] * input[i+j*w];
}
]]



cl.compile_source("square", code)
local ker = cl.get_kernel("square")

local data1 = Array{
	components = 1,
	dim = {4, 4}
}
local data2 = Array{
	components = 1,
	dim = data1.dim
}

for i=0, data1.dim[1]-1 do
for j=0, data1.dim[2]-1 do
	data1:setcell(i, j, i+j)
	data2:setcell(i, j, 0)
end
end

local mem1 = cl.create_memory_buffer(CL.MEM_READ_WRITE, data1)
local mem2 = cl.create_memory_buffer(CL.MEM_READ_WRITE, data2)

cl.execute(cl.gpu_device(), ker, #data1.dim, data1.dim, mem1, mem2)
cl.enqueue_read(cl.gpu_device(), mem2, true, 0, data2)


print("\ndata2:")
print_array(data2)