/**
 * Copyright (c) 2014 Google, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.google.fpl.liquidfunpaint.renderer;

import com.google.fpl.liquidfunpaint.LiquidWorld;
import com.google.fpl.liquidfunpaint.SolidWorld;
import com.google.fpl.liquidfunpaint.shader.ShaderProgram;
import com.google.fpl.liquidfunpaint.util.DrawableLayer;
import com.google.fpl.liquidfunpaint.util.Observable;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

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

/**
 * GameLoop class. Contains the game update and render loop.
 *
 * This also contains the pointer to the LiquidFun world. The convention for
 * thread-safety is to called acquireWorld to obtain a thread-safe world
 * pointer, and releaseWorld when you are done with the object.
 */
public class GameLoop extends Observable<Float> implements DrawableLayer {
    // Private constants
    private static final GameLoop _instance = new GameLoop();
    public static final boolean DEBUG_DRAW = true;

    private static final float TIME_STEP = 1 / 60f; // 60 fps

    // Public static constants; variables for reuse
    public static final float MAT4X4_IDENTITY[];

    // Public constants; records render states
    public int sScreenWidth = 1;
    public int sScreenHeight = 1;

    /// Member variables
    private Context mContext = null;

    // GameLoop class owns all Box2D objects, for thread-safety
    // Variables for thread synchronization
    private volatile boolean mSimulation = false;

    final private Queue<Runnable> pendingRunnables = new ConcurrentLinkedQueue<>();

    static {
        MAT4X4_IDENTITY = new float[16];
        Matrix.setIdentityM(MAT4X4_IDENTITY, 0);
    }


    @Override
    protected void finalize() {
        LiquidWorld.getInstance().deleteWorld();
    }

    private GameLoop() {
    }

    public static GameLoop getInstance() {
        return _instance;
    }

    @Override
    public void init(Context context) {
        mContext = context;

        LiquidWorld.getInstance().init(context);

        reset();

        startSimulation();
    }

    /**
     * Resets the world -- which means a delete and a new.
     * Initializes the boundaries and reset the ParticleRenderer as well.
     */
    @Override
    public void reset() {
        clearPhysicsCommands();
        LiquidWorld.getInstance().reset();
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        if (mSimulation) {

            while (!pendingRunnables.isEmpty()) {
                pendingRunnables.poll().run();
            }

            setChanged();
            notifyObservers();

            LiquidWorld.getInstance().update(TIME_STEP);

            GLES20.glClearColor(1, 1, 1, 1);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

            // Draw particles
            LiquidWorld.getInstance().onDrawFrame(gl);

            SolidWorld.getInstance().onDrawFrame(gl);
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        sScreenWidth = width;
        sScreenHeight = height;

        GLES20.glViewport(0, 0, width, height);

        LiquidWorld.getInstance().onSurfaceChanged(gl, width, height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        if (!LiquidWorld.getInstance().hasWorld()) {
            throw new IllegalStateException("Init world before rendering");
        }

        ShaderProgram.loadAllShaders(mContext.getAssets());

        TextureRenderer.getInstance().onSurfaceCreated();

        LiquidWorld.getInstance().onSurfaceCreated(gl, config);

        SolidWorld.getInstance().init(mContext);
    }

    public void pauseSimulation() {
        mSimulation = false;
    }

    public void startSimulation() {
        mSimulation = true;
    }

    public void addPhysicsCommand(Runnable runnable){
        pendingRunnables.add(runnable);
    }

    public void clearPhysicsCommands(){
        pendingRunnables.clear();
    }

}
@@ -2203,24 +2203,83 @@
 n = false;%0A%0A
+    LiquidWorld mLiquidWorld;%0A    SolidWorld mSolidWorld;%0A%0A
     final pr
@@ -2508,32 +2508,33 @@
 ize() %7B%0A        
+m
 LiquidWorld.getI
@@ -2529,30 +2529,16 @@
 idWorld.
-getInstance().
 deleteWo
@@ -2741,32 +2741,47 @@
 ontext;%0A%0A       
+ mLiquidWorld =
  LiquidWorld.get
@@ -2794,21 +2794,91 @@
 ce()
-.init(context
+;%0A        mLiquidWorld.init(context);%0A        mSolidWorld = SolidWorld.getInstance(
 );%0A%0A
@@ -3150,32 +3150,33 @@
 ands();%0A        
+m
 LiquidWorld.getI
@@ -3171,29 +3171,44 @@
 idWorld.
-getInstance()
+reset();%0A        mSolidWorld
 .reset()
@@ -3471,32 +3471,33 @@
 );%0A%0A            
+m
 LiquidWorld.getI
@@ -3492,30 +3492,16 @@
 idWorld.
-getInstance().
 update(T
@@ -3648,32 +3648,33 @@
 les%0A            
+m
 LiquidWorld.getI
@@ -3665,38 +3665,24 @@
 LiquidWorld.
-getInstance().
 onDrawFrame(
@@ -3691,32 +3691,33 @@
 );%0A%0A            
+m
 SolidWorld.getIn
@@ -3711,30 +3711,16 @@
 idWorld.
-getInstance().
 onDrawFr
@@ -3938,32 +3938,33 @@
 ight);%0A%0A        
+m
 LiquidWorld.getI
@@ -3955,38 +3955,24 @@
 LiquidWorld.
-getInstance().
 onSurfaceCha
@@ -4092,16 +4092,17 @@
    if (!
+m
 LiquidWo
@@ -4105,30 +4105,16 @@
 idWorld.
-getInstance().
 hasWorld
@@ -4330,24 +4330,25 @@
 );%0A%0A        
+m
 LiquidWorld.
@@ -4347,30 +4347,16 @@
 idWorld.
-getInstance().
 onSurfac
@@ -4382,24 +4382,25 @@
 );%0A%0A        
+m
 SolidWorld.g
@@ -4398,30 +4398,16 @@
 idWorld.
-getInstance().
 init(mCo

Prepare for dependency injection.