package com.robotium.solo;

import java.nio.IntBuffer;
import java.util.concurrent.CountDownLatch;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.view.View;

/**
 * Used to wrap and replace the renderer to gain access to the gl context.  
 * 
 * @author Per-Erik Bergman, bergman@uncle.se
 * 
 */

class GLRenderWrapper implements Renderer {

	private Renderer renderer;
	private int width;
	private int height;
	private final GLSurfaceView view;
	private CountDownLatch latch;
	private boolean takeScreenshot = true;
	private int glVersion;

	/**
	 * Constructs this object.
	 * 
	 * @param view the current glSurfaceView
	 * @param renderer the renderer to wrap
	 * @param latch the count down latch
	 */
	
	public GLRenderWrapper(GLSurfaceView view,
			Renderer renderer, CountDownLatch latch) {
		this.view = view;
		this.renderer = renderer;
		this.latch = latch;
		
		this.width = view.getWidth();
		this.height = view.getHeight();
		
		Integer out = new Reflect(view).field("mEGLContextClientVersion")
				.out(Integer.class);
		if ( out != null ) {
			this.glVersion = out.intValue();
		} else {
			this.glVersion = -1;
			this.takeScreenshot = false;
		}
	}

	@Override
	/*
	 * (non-Javadoc)
	 * @see android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig)
	 */
	
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		renderer.onSurfaceCreated(gl, config);
	}

	@Override
	/*
	 * (non-Javadoc)
	 * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)
	 */
	
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		this.width = width;
		this.height = height;
		renderer.onSurfaceChanged(gl, width, height);
	}

	@Override
	/*
	 * (non-Javadoc)
	 * @see android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.opengles.GL10)
	 */
	
	public void onDrawFrame(GL10 gl) {
		renderer.onDrawFrame(gl);
		if (takeScreenshot) {
			Bitmap screenshot = null;

			if (glVersion >= 2) {
				screenshot = savePixels(0, 0, width, height);
			} else {
				screenshot = savePixels(0, 0, width, height, gl);
			}

			new Reflect(view).field("mDrawingCache").type(View.class)
					.in(screenshot);
			latch.countDown();
			takeScreenshot = false;
		}
	}

	/**
	 * Tell the wrapper to take a screen shot 
	 */
	
	public void setTakeScreenshot() {
		takeScreenshot = true;
	}

	/**
	 * Set the count down latch 
	 */

	public void setLatch(CountDownLatch latch) {
		this.latch = latch;
	}

	/**
	 * Extract the bitmap from OpenGL 
	 * 
	 * @param x the start column
	 * @param y the start line
	 * @param w the width of the bitmap
	 * @param h the height of the bitmap
	 */
	
	private Bitmap savePixels(int x, int y, int w, int h) {
		int b[] = new int[w * (y + h)];
		int bt[] = new int[w * h];
		IntBuffer ib = IntBuffer.wrap(b);
		ib.position(0);
		GLES20.glReadPixels(x, 0, w, y + h, GLES20.GL_RGBA,
				GLES20.GL_UNSIGNED_BYTE, ib);

		for (int i = 0, k = 0; i < h; i++, k++) {
			// remember, that OpenGL bitmap is incompatible with Android bitmap
			// and so, some correction need.
			for (int j = 0; j < w; j++) {
				int pix = b[i * w + j];
				int pb = (pix >> 16) & 0xff;
				int pr = (pix << 16) & 0x00ff0000;
				int pix1 = (pix & 0xff00ff00) | pr | pb;
				bt[(h - k - 1) * w + j] = pix1;
			}
		}

		Bitmap sb = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
		return sb;
	}

	/**
	 * Extract the bitmap from OpenGL 
	 * 
	 * @param x the start column
	 * @param y the start line
	 * @param w the width of the bitmap
	 * @param h the height of the bitmap
	 * @param gl the current GL reference
	 */
	
	private static Bitmap savePixels(int x, int y, int w, int h, GL10 gl) {
		int b[] = new int[w * (y + h)];
		int bt[] = new int[w * h];
		IntBuffer ib = IntBuffer.wrap(b);
		ib.position(0);
		gl.glReadPixels(x, 0, w, y + h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);

		for (int i = 0, k = 0; i < h; i++, k++) {
			// remember, that OpenGL bitmap is incompatible with Android bitmap
			// and so, some correction need.
			for (int j = 0; j < w; j++) {
				int pix = b[i * w + j];
				int pb = (pix >> 16) & 0xff;
				int pr = (pix << 16) & 0x00ff0000;
				int pix1 = (pix & 0xff00ff00) | pr | pb;
				bt[(h - k - 1) * w + j] = pix1;
			}
		}

		Bitmap sb = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
		return sb;
	}

}
