package ro.mirceanistor.gltestutils;

import android.app.Instrumentation;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.support.test.InstrumentationRegistry;
import android.view.WindowManager;

import java.util.concurrent.CountDownLatch;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by mirceanis on 2016-05-06.
 * <p>
 * A utility class that provides a way to enqueue runnables on a GLThread during an instrumentation test
 * </p>
 */
public class GLTestUtils {

    private static GLTestUtils mInstance;
    private GL10 mGL;

    /**
     * Essentially this is used to enqueue events on it's associated GLThread
     */
    private GLSurfaceView mGLSurfaceView;
    private Context testContext = InstrumentationRegistry.getContext();
    private Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();

    protected GLTestUtils() throws Exception {

        instrumentation.runOnMainSync(new Runnable() {
            @Override
            public void run() {
                mGLSurfaceView = new GLSurfaceView(testContext);
                mGLSurfaceView.setId(R.id.z_gl_surface_view_id_used_for_testing);
                mGLSurfaceView.setEGLContextClientVersion(2);

                // Attach the renderer to the view
                mGLSurfaceView.setRenderer(new DummyRenderer());
                mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

                WindowManager wm = (WindowManager) testContext.getSystemService(Context.WINDOW_SERVICE);
                wm.addView(mGLSurfaceView, new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST,0));
            }
        });

        // Wait for the renderer to get the GL context.
        synchronized (mGlReadyLock) {
            while (mGL == null) {
                mGlReadyLock.wait();
            }
        }
    }

    /**
     * Tell android that you're not into GLThreading any more so allow this to be cleaned up.
     */
    public static void release() {
        mInstance.detachView();
        mInstance = null;
    }

    /**
     * Posts your {@code payload} on a GLThread and blocks until it's finished running
     * @param payload the Runnable to run
     * @throws Exception if initialized() hasn't been called
     */
    public static void runOnGLThreadAndWait(Runnable payload) throws Exception {

        if (mInstance == null) {
            mInstance = new GLTestUtils();
        }

        mInstance.doRunOnGLThread(payload);
    }

    private void doRunOnGLThread(final Runnable payload) throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(1);
        mGLSurfaceView.queueEvent(new Runnable() {
            public void run() {
                payload.run();
                latch.countDown();
            }
        });
        latch.await();  // wait for the payload to finish
    }

    private void detachView() {
        instrumentation.runOnMainSync(new Runnable() {
            @Override
            public void run() {
                WindowManager wm = (WindowManager) testContext.getSystemService(Context.WINDOW_SERVICE);
                wm.removeView(mGLSurfaceView);
            }
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // dummy renderer
    ///////////////////////////////////////////////////////////////////////////


    /**
     * Dummy renderer, exposes the GL context
     */
    private static final Object mGlReadyLock = new Object();

    private class DummyRenderer implements GLSurfaceView.Renderer {

        @Override
        public void onDrawFrame(GL10 gl) {

        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {

        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            synchronized (mGlReadyLock) {
                mGL = gl;
                mGlReadyLock.notifyAll();
            }
        }
    }

}
@@ -536,16 +536,44 @@
 /p%3E%0A */%0A
+@SuppressWarnings(%22unused%22)%0A
 public c
@@ -956,16 +956,73 @@
 ation();
+%0A    private int mWidth = 0;%0A    private int mHeight = 0;
 %0A%0A    pr
@@ -1707,34 +1707,49 @@
     
-wm.addView(mGLSurfaceView,
+WindowManager.LayoutParams layoutParams =
  new
@@ -1816,16 +1816,73 @@
 TOAST,0)
+;%0A                wm.addView(mGLSurfaceView, layoutParams
 );%0A     
@@ -2727,32 +2727,396 @@
 ayload);%0A    %7D%0A%0A
+    /**%0A     * Gets the width of the underlying GLSurfaceView%0A     * @return the width in pixels%0A     */%0A    public static int getWidth() %7B%0A        return mInstance.mWidth;%0A    %7D%0A%0A    /**%0A     * Gets the height of the underlying GLSurfaceView%0A     * @return the height in pixels%0A     */%0A    public static int getHeight() %7B%0A        return mInstance.mHeight;%0A    %7D%0A%0A
     private void
@@ -4351,36 +4351,28 @@
 ight) %7B%0A
-%0A
     
-    %7D%0A%0A
         
 @Overrid
@@ -4367,125 +4367,95 @@
     
-@Override%0A        public void onSurfaceCreated(GL10 gl, EGLConfig config) %7B%0A            synchronized (mGlReadyLock) %7B
+synchronized (mGlReadyLock) %7B%0A                mGL = gl;%0A                mWidth = width;
 %0A   
@@ -4460,39 +4460,47 @@
                m
-GL = gl
+Height = height
 ;%0A              
@@ -4542,25 +4542,121 @@
      %7D%0A        %7D
+%0A%0A        @Override%0A        public void onSurfaceCreated(GL10 gl, EGLConfig config) %7B%0A%0A        %7D
 %0A    %7D%0A%0A%7D

add `getWidth` and `getHeight` methods to aid in testing