package org.kne.opencl64;

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.ref.Cleaner;

import club.doki7.ffm.ptr.BytePtr;
import club.doki7.ffm.ptr.DoublePtr;
import club.doki7.ffm.ptr.FloatPtr;
import club.doki7.ffm.ptr.IntPtr;
import club.doki7.ffm.ptr.LongPtr;
import club.doki7.ffm.ptr.PointerPtr;
import club.doki7.ffm.ptr.ShortPtr;
import club.doki7.opencl.handle.CLKernel;
public class OpenCLKernel {
	private OpenCLProgram program;
	private CLKernel cl_kernel;
	private String name;
	private int argindex=0;

	private static final Cleaner clr=Cleaner.create();
	
	
	public OpenCLProgram getProgram() {
		return program;
	}

	public CLKernel getCl_kernel() {
		return cl_kernel;
	}

	public String getName() {
		return name;
	}

	public OpenCLKernel(OpenCLProgram program,String name) {
		this.program=program;
		this.name=name;
		IntPtr errcode=IntPtr.allocate(Arena.ofAuto());
		cl_kernel=OpenCLPlatform.opencl.createKernel(program.getCl_program(), BytePtr.allocateString(Arena.ofAuto(), name), errcode);
		 CLUtils.checkResult(errcode);
		regCleaner();
	}
	private void regCleaner() {
		releaser=new OpenCLKernelReleaser(cl_kernel);
		clr.register(this, releaser);
	}

	protected OpenCLKernel(OpenCLProgram program,CLKernel cl_kernel) {
		this.program=program;
		this.cl_kernel=cl_kernel;
	}
	public void resetArgIndex() {
		argindex=0;
	}
	
	public OpenCLKernel putArg(OpenCLMemory omem) {
		return setArg(argindex++,omem);
	}
	public OpenCLKernel putArg(int val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putArg(long val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putArg(float val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putArg(double val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putArg(short val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putArg(byte val) {
		return setArg(argindex++,val);
	}
	public OpenCLKernel putLocalMemoryArg(long length) {
		return setLocalMemoryArg(argindex++,length);
	}
	
	private OpenCLKernel setLocalMemoryArg(int index, long length) {
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,length, MemorySegment.NULL);
		 CLUtils.checkResult(errcode);
		return this;
	}

	public OpenCLKernel setArg(int index,OpenCLMemory omem) {
		PointerPtr pptr=PointerPtr.allocate(Arena.ofAuto());
		pptr.write(omem.getCl_mem());
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,int val) {
		IntPtr pptr=IntPtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,long val) {
		LongPtr pptr=LongPtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,float val) {
		FloatPtr pptr=FloatPtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,double val) {
		DoublePtr pptr=DoublePtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,short val) {
		ShortPtr pptr=ShortPtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	public OpenCLKernel setArg(int index,byte val) {
		BytePtr pptr=BytePtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,pptr.segment().byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}
	
	/*public OpenCLKernel setArg(int index,boolean val) {
		PointerPtr pptr=PointerPtr.allocate(Arena.ofAuto());
		pptr.write(val);
		int errcode=OpenCLPlatform.opencl.setKernelArg(cl_kernel, index,ValueLayout.ADDRESS.byteSize(), pptr.segment());
		 CLUtils.checkResult(errcode);
		return this;
	}*/
	
	private OpenCLKernelReleaser releaser;
	public void close() {
		releaser.run();
	}
	
	public boolean isClosed() {
		return releaser.isReleased();
	}

	
}
class OpenCLKernelReleaser extends Releaser<CLKernel>{

	public OpenCLKernelReleaser(CLKernel resource) {
		super(resource);
	}

	@Override
	protected void release(CLKernel resource) {
		int errcode=OpenCLPlatform.opencl.releaseKernel(resource);
		 CLUtils.checkResult(errcode);
	}

}
