package sytar.processing;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.File;
import java.io.IOException;
import java.nio.FloatBuffer;

import javax.imageio.ImageIO;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

import com.jogamp.opencl.CLBuffer;
import com.jogamp.opencl.CLCommandQueue;
import com.jogamp.opencl.CLDevice;
import com.jogamp.opencl.CLKernel;
import com.jogamp.opencl.CLPlatform;
import com.jogamp.opencl.CLProgram;
import com.jogamp.opencl.CLMemory.Mem;
import com.jogamp.opencl.gl.CLGLContext;
import com.jogamp.opencl.gl.CLGLImage2d;
import com.jogamp.opencl.gl.CLGLTexture2d;
import com.jogamp.opengl.util.awt.TextureRenderer;
import com.jogamp.opengl.util.texture.Texture;

/**
 * @author ABA
 * 
 */
public class BitMap {
	
	private static final Color	BACKGROUND_COLOR= Color.WHITE;
	private GL2				gl;
	private BufferedImage	img;
	private TextureRenderer	textureRenderer;
	private boolean			ready				= false;
	private Graphics2D		g2;
	private CLGLContext		cl;
    CLGLTexture2d			fullTex;
    CLGLImage2d				fullImg;
    CLBuffer<FloatBuffer>	fullBuffer;
	private CLCommandQueue	queue;
	private CLKernel	kernel;

	/**
	 * @return true if the object is ready to be written into
	 */
	public boolean isReady () {
		return ready;
	}

	/**
	 * Create a bitmap image with provided dimensions.
	 * @param width
	 *            the width of the image
	 * @param height
	 *            the height of the image
	 */
	public BitMap (int width, int height) {
		img = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);

		for (int currentWidth = 0; currentWidth < img.getWidth(); ++currentWidth)
			for (int currentHeight = 0; currentHeight < img.getHeight(); ++currentHeight)
				img.setRGB(currentWidth, currentHeight, BACKGROUND_COLOR.getRGB());
		
		ready = true;
	}
	
	/**
	 * Set the GL context for the bitmap
	 * @param drawable the gl context
	 */
	public void setGL (GLAutoDrawable drawable) {
		this.gl = drawable.getGL().getGL2();
		textureRenderer = new TextureRenderer(img.getWidth(), img.getHeight(), false);
		textureRenderer.markDirty(0, 0, img.getWidth(), img.getHeight());
		g2 = textureRenderer.createGraphics();
		
		setCL(drawable);
	}

	/**
	 * @param drawable
	 */
	private void setCL (GLAutoDrawable drawable) {
		//CLDevice device = CLPlatform.getDefault(CLPlatformFilters.type(CLDevice.Type.GPU)).getMaxFlopsDevice();
		CLPlatform[] platform = CLPlatform.listCLPlatforms();
		CLDevice device = platform[1].getMaxFlopsDevice();
        cl = CLGLContext.create(drawable.getContext(), device);
        
        CLDevice devices[] = cl.getDevices();
        queue = devices[0].createCommandQueue();
        
        fullTex = null;
        fullBuffer = cl.createFloatBuffer(img.getWidth() * img.getHeight(), Mem.READ_WRITE);

        CLProgram prog = null;
		try {
			prog = cl.createProgram(sytar.opengl.Renderer.readFileAsString("bin/sytar/processing/processTexture.cl")).build();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        kernel = prog.createCLKernel("processTexture");
	}
	
	/**
	 * Save the current image to the filename given. PNG format is used.
	 * 
	 * @param filename
	 *            the filename
	 * @return true if the operation is a success
	 */
	public boolean save (String filename) {
		File file = new File(filename);
		try {
			ImageIO.write(img, "png", file);
		} catch (IOException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * @return a reference to the buffered image
	 */
	public BufferedImage GetBuffImg () {
		return img;
	}

	/**
	 * @return a reference to the OpenGL texture created with the image
	 */
	public Texture getTexture () {
		BufferedImageOp op = new AffineTransformOp(new AffineTransform(), AffineTransformOp.TYPE_BICUBIC);
		g2.drawImage(img, op , 0, 0);
		fullTex = cl.createFromGLTexture2d(textureRenderer.getTexture().getTarget(),
				textureRenderer.getTexture().getTextureObject(), 0, Mem.READ_WRITE);
		return textureRenderer.getTexture();
	}
}