package mcmas.core;

import org.jocl.CL;
import org.jocl.Pointer;
import org.jocl.Sizeof;
import org.jocl.cl_mem;

/**
 * MCM representation for an opaque OpenCL cl_mem memory buffer.
 */
public class MCMMem extends MCMObject {
	
	private boolean local;
	private boolean readOnly;
	private final cl_mem mem;
	private long size = -1;
	
	/**
	 * Create a new MCMMem object from a native cl_mem reference.
	 * 
	 * @param mem memory buffer to wrap
	 */
	public MCMMem(cl_mem mem) {
		this.local = false;
		this.mem = mem;
	}
	
	/**
	 * Create a new MCMMem object from a native cl_mem reference
	 * with an explicit size.
	 * 
	 * @param mem memory buffer to wrap
	 * @param size explicit size for the memory buffer
	 */
	public MCMMem(cl_mem mem, long size) {
		this.mem = mem;
		this.size = size;
	}
	
	/**
	 * Mark the buffer as a local OpenCL buffer.
	 * This type of buffer is only usable in the kernel and must have a null
	 * mem attribute (i.e. it cannot be initialized).
	 */
	public void setLocal(boolean local) {
		this.local = local;
	}
	
	/**
	 * Check if the buffer is local.
	 */
	public boolean isLocal() {
		return local;
	}
	
	/**
	 * Mark the buffer as a read-only (constant) OpenCL buffer.
	 * This type of buffer can only be written on the CPU, not in the kernel.
	 */
	public void setReadOnly(boolean readOnly) {
		this.readOnly = readOnly;
	}
	
	/**
	 * Check if the buffer is read-only
	 */
	public boolean isReadOnly() {
		return readOnly;
	}
	
	/**
	 * Retrieve the wrapped cl_mem reference. Allows usage of MCMMem objet
	 * in JOCL methods.
	 * @return the wrapped cl_mem reference
	 */
	public cl_mem getMem() {
		return mem;
	}
	
	/**
	 * Get wrapped cl_mem memory size using an OpenCL request
	 * or using the cached value.
	 * @return the buffer size in bytes
	 */
	public long getSize() {
		if (size == -1) {
			long[] nsize = new long[1];
			CL.clGetMemObjectInfo(mem, CL.CL_MEM_SIZE, Sizeof.cl_long, Pointer.to(nsize), null);
			size = nsize[0];
		}
		
		return size;
	}
	
	/**
	 * Release the native memory associated to this object.
	 */
	public void releaseImpl() {
		CL.clReleaseMemObject(mem);
	}

}
