content: package edu.stanford.rsl.conrad.data;
  
  import java.nio.FloatBuffer;
  
  import com.jogamp.opencl.CLBuffer;
  import com.jogamp.opencl.CLCommandQueue;
  import com.jogamp.opencl.CLContext;
  import com.jogamp.opencl.CLDevice;
  
  public abstract class OpenCLMemoryDelegate implements AutoCloseable{
  	
  	protected CLBuffer<FloatBuffer> fBuffer;
  	protected float [] linearHostMemory;
  	protected CLContext context;
  	protected CLDevice device;
  	protected boolean hostChanged;
  	protected boolean deviceChanged;
  	
  	boolean debug = false;
  	
  	
  	/**
  	 * Returns the pointer the the CLBuffer object.
  	 * @return the CLBuffer
  	 */
  	public CLBuffer<FloatBuffer> getCLBuffer(){
  		return fBuffer;
  	}
  	
  	public float[] getBuffer(){
  		return linearHostMemory;
  	}
  
  	/**
  	 * Call this method before you want to run a host operation on the grid.
  	 */
  	public void prepareForHostOperation() {
  		if (deviceChanged && hostChanged) throw new RuntimeException("Memory is in inconsistent state. Host and Device have changed independently.");
  		if (deviceChanged){
  			pull();
  			deviceChanged = false;
  		}
  	}
  	
  	/**
  	 * Call this method before you want to run a device operation.
  	 */
  	public void prepareForDeviceOperation() {
  		if (deviceChanged && hostChanged) throw new RuntimeException("Memory is in inconsistent state. Host and Device have changed independently.");
  		if (hostChanged){
  			push();
  			hostChanged = false;
  		}
  	}
  
  	/**
  	 * This method sends the current data from the host memory to the device memory.
  	 */
  	public void push() {
  		if (debug) System.out.println("writing to device");
  		copyToLinearHostMemory();
  		fBuffer.getBuffer().put(linearHostMemory);
  		fBuffer.getBuffer().rewind();
  		CLCommandQueue queue = device.createCommandQueue();
  		queue.putWriteBuffer(fBuffer, true).finish();
  		queue.release();
  		fBuffer.getBuffer().rewind();
  	}
  
  	/**
  	 * This method fetches the current data from the device memory to the host memory.
  	 */
  	public void pull() {
  		if (debug) System.out.println("writing to host");
  		fBuffer.getBuffer().rewind();
  		CLCommandQueue queue = device.createCommandQueue();
  		queue.putReadBuffer(fBuffer, true).finish();
  		queue.release();
  		fBuffer.getBuffer().get(linearHostMemory);
  		fBuffer.getBuffer().rewind();
  		copyFromLinearHostMemory();
  	}
  
  	/**
  	 * Call this method after you performed changes in the device memory, e.g. by calling a kernel function.
  	 */
  	public void notifyDeviceChange(){
  		deviceChanged = true;
  	}
  	
  	public void notifyHostChange(){
  		hostChanged = true;
  	}
  	
  	/**
  	 * release the memory from the device
  	 */
  	public void release() {
  		if (fBuffer!=null && !fBuffer.isReleased())
  			fBuffer.release();
  	}
  
  	public CLDevice getCLDevice() {
  		return device;
  	}
  
  	public CLContext getCLContext() {
  		return context;
  	}
  	
  	@Override
  	public void close(){
  		if(debug == true)
  			System.out.println("Was at the autocloseable close() function!");
  		this.release();
  	}
  	
  	protected abstract void copyToLinearHostMemory();
  	
  	protected abstract void copyFromLinearHostMemory();
  
  }
  