/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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.example.issm;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

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

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

public class MyGLRenderer implements GLSurfaceView.Renderer 
{
	public volatile float mAngle;
	public volatile float mAngleX; /* NEW STUFF*/
	public volatile float mAngleY;
	
	
    private FloatBuffer[] triangleVert;
	
	private float[][] mScaleMatrix;  // scaling /* NEW STUFF*/
	private float[][] mRotXMatrix;	 // rotation x 
	private float[][] mRotYMatrix;	 // rotation x

	/**
	 * Store the model matrix. This matrix is used to move models from object space (where each model can be thought
	 * of being located at the center of the universe) to world space.
	 */
	private float[][] mModelMatrix;
	
	/**
	 * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
	 * it positions things relative to our eye.
	 */
	private float[][] mViewMatrix;

	/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
	private float[][] mProjectionMatrix;

	/** Allocate storage for the final combined matrix. This will be passed into the shader program. */
	private float[][] mMVPMatrix;

	/** This will be used to pass in the transformation matrix. */
	private int mMatrixHandle;
	
    private float[][] triangleData2DArr;
    
    private int mPositionHandle;
    
    private int mColorHandle;
    
	/** How many bytes per float. */
    private final int mBytesPerFloat = 4;

    /** How many elements per vertex. */
    private final int mStrideBytes = 7 * mBytesPerFloat;

    /** Offset of the position data. */
    private final int mPositionOffset = 0;

    /** Size of the position data in elements. */
    private final int mPositionDataSize = 3;

    /** Offset of the color data. */
    private final int mColorOffset = 3;

    /** Size of the color data in elements. */
    private final int mColorDataSize = 4;	

	// scaling /* NEW STUFF*/
	float scaleX = 1.0f;
	float scaleY = 1.0f;
	float scaleZ = 1.0f;

	public float eyeX;
	public float eyeY;
	public float eyeZ;
	
    public MyGLRenderer(float[][] vertices)
    {	
    	triangleData2DArr = new float[vertices.length][21];
 
    	triangleVert = new FloatBuffer[vertices.length];
    	mScaleMatrix  = new float[triangleVert.length][16];  // scaling  /* NEW STUFF*/
    	mRotXMatrix  = new float[triangleVert.length][16];	 // rotation x
    	mRotYMatrix  = new float[triangleVert.length][16];	 // rotation x
    	
    	for (int i = 0; i < vertices.length; i++)
    	{
    		for (int j = 0; j < 21; j++)
    			triangleData2DArr[i][j] = vertices[i][j]; 
    	}
	    
	    // initialize vertex byte buffer for shape coordinates
    	for (int i = 0; i < vertices.length; i++)
    	{
    		triangleVert[i] = ByteBuffer.allocateDirect(triangleData2DArr[i].length * mBytesPerFloat)
    													.order(ByteOrder.nativeOrder()).asFloatBuffer();
    		triangleVert[i].put(triangleData2DArr[i]).position(0);
    	}
    }
    
	public void changeScale(float scale) /* NEW STUFF*/
	{
		if (scaleX * scale > 3.0f) return;
		  scaleX *= scale; scaleY *= scale; scaleZ *= scale;
		
		//scaleX += scale;scaleY += scale;scaleZ += scale;
		//scaleX = scale;scaleY = scale;scaleZ = scale;
		//scaleX /= 100;scaleY /= 100;scaleZ /= 100;
	}

	public void defaultScale() 
	{
		scaleX = 1f;
		scaleY = 1f;
		scaleZ = 1f;
	}
	
    public void onSurfaceCreated(GL10 unused, EGLConfig config) 
    {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        
		// Position the eye behind the origin.
		eyeX = 0.0f;
		eyeY = 0.0f;
		eyeZ = 1.5f;

		// We are looking toward the distance
		final float lookX = 0.0f;
		final float lookY = 0.0f;
		final float lookZ = -5.0f;

		// Set our up vector. This is where our head would be pointing were we holding the camera.
		final float upX = 0.0f;
		final float upY = 1.0f;
		final float upZ = 0.0f;

		// Set the view matrix. This matrix can be said to represent the camera position.
		// NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
		// view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
		mViewMatrix = new float[triangleVert.length][16];
		
		
		for (int i = 0; i < triangleVert.length; i++)
			Matrix.setLookAtM(mViewMatrix[i], 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
			//Matrix.setLookAtM(mVMatrix[i], 0, 0, 0, -5.0f, 0.0f, 0f, 0f, 0f, 1.0f, 0.0f);
        
		final String vertexShader =
				"uniform mat4 u_MVPMatrix;      \n"		// A constant representing the combined model/view/projection matrix.
			  + "attribute vec4 a_Position;     \n"		// Per-vertex position information we will pass in.
			  + "attribute vec4 a_Color;        \n"		// Per-vertex color information we will pass in.			  
			  + "varying vec4 v_Color;          \n"		// This will be passed into the fragment shader.
			  + "void main()                    \n"		// The entry point for our vertex shader.
			  + "{                              \n"
			  + "   v_Color = a_Color;          \n"		// Pass the color through to the fragment shader. 
			  
			  											// It will be interpolated across the triangle.
			  + "   gl_Position = u_MVPMatrix   \n" 	// gl_Position is a special variable used to store the final position.
			  + "               * a_Position;   \n"     // Multiply the vertex by the matrix to get the final point in 			                                            			 
			  + "}                              \n";    // normalized screen coordinates.

			final String fragmentShader =
				"precision mediump float;       \n"		// Set the default precision to medium. We don't need as high of a											// precision in the fragment shader.				
			  + "varying vec4 v_Color;          \n"		// This is the color from the vertex shader interpolated across the 
			  											// triangle per fragment.			  
			  + "void main()                    \n"		// The entry point for our fragment shader.
			  + "{                              \n"
			  + "   gl_FragColor = v_Color;     \n"		// Pass the color directly through the pipeline.		  
			  + "}                              \n";												

    	   
        int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		// Pass in the shader source.
		GLES20.glShaderSource(vertexShaderHandle, vertexShader);

		// Compile the shader.
		GLES20.glCompileShader(vertexShaderHandle);

		// Get the compilation status.
		final int[] compileStatus1 = new int[1];
		GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus1, 0);

		// If the compilation failed, delete the shader.
		if (compileStatus1[0] == 0) 
		{				
			GLES20.glDeleteShader(vertexShaderHandle);
			vertexShaderHandle = 0;
		}
		
         
    	int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
		// Pass in the shader source.
		GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

		// Compile the shader.
		GLES20.glCompileShader(fragmentShaderHandle);

		// Get the compilation status.
		final int[] compileStatus2 = new int[1];
		GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus2, 0);

		// If the compilation failed, delete the shader.
		if (compileStatus2[0] == 0) 
		{				
			GLES20.glDeleteShader(fragmentShaderHandle);
			fragmentShaderHandle = 0;
		}

        
        int mProgram = GLES20.glCreateProgram();
		// Bind the vertex shader to the program.
		GLES20.glAttachShader(mProgram, vertexShaderHandle);			

		// Bind the fragment shader to the program.
		GLES20.glAttachShader(mProgram, fragmentShaderHandle);

		// Bind attributes
		GLES20.glBindAttribLocation(mProgram, 0, "a_Position");
		GLES20.glBindAttribLocation(mProgram, 1, "a_Color");
		
		// Link the two shaders together into a program.
		GLES20.glLinkProgram(mProgram);

		// Get the link status.
		final int[] linkStatus = new int[1];
		GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);

		// If the link failed, delete the program.
		if (linkStatus[0] == 0) 
		{				
			GLES20.glDeleteProgram(mProgram);
			mProgram = 0;
		}
		
		for (int i = 0; i < triangleVert.length; i++)
		{
			mMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_MVPMatrix");       
	        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "a_Position");
	        mColorHandle = GLES20.glGetAttribLocation(mProgram, "a_Color");
		}
        
        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);
    }
    public void onSurfaceChanged(GL10 unused, int width, int height) 
    {
        // Adjust the viewport based on geometry changes,
        // such as screen rotation
        GLES20.glViewport(0, 0, width, height);
        
		// Create a new perspective projection matrix. The height will stay the same
		// while the width will vary as per aspect ratio.
		final float ratio = (float) width / height;
		
		mProjectionMatrix = new float[triangleVert.length][16];
		for (int i = 0; i < triangleVert.length; i++)
			Matrix.frustumM(mProjectionMatrix[i], 0, -ratio*0.7f, ratio*1.0f, 0.0f, 1.0f, 1.0f, 14f);
    }
    
    public void onDrawFrame(GL10 glUnused) 
    {
    	// Draw background color
    	GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    	
    	mModelMatrix = new float[triangleVert.length][16];
        // Draw the triangle facing straight on.
    	for (int i = 0; i < triangleVert.length; i++)
    	{
    		//Matrix.setIdentityM(mModelMatrix[i], 0); /* NEW STUFF*/
    		Matrix.setIdentityM(mScaleMatrix[i], 0);
    		Matrix.scaleM(mScaleMatrix[i], 0, scaleX, scaleY, scaleZ);
    		
    		// Rotation along x
    		Matrix.setRotateM(mRotXMatrix[i], 0, this.mAngleY, -1.0f, 0.0f, 0.0f);
    		Matrix.setRotateM(mRotYMatrix[i], 0, this.mAngleX, 0.0f, 1.0f, 0.0f); /* NEW STUFF*/
    		
    		Matrix.setLookAtM(mViewMatrix[i], 0, eyeX, eyeY, eyeZ, 0.0f, 0.0f, -5.0f, 0.0f, 1.0f, 0.0f); 
    	
    	}
    		
    	mMVPMatrix = new float[triangleVert.length][16];
    	draw(triangleVert); 

    }

    private void draw(final FloatBuffer[] aTriangleBuffer)
    {
    	for (int i = 0; i < aTriangleBuffer.length; i++)
    	{
	        // Pass in the position information
	        aTriangleBuffer[i].position(mPositionOffset);
	        GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
	                				     mStrideBytes, aTriangleBuffer[i]);
	        
	        GLES20.glEnableVertexAttribArray(mPositionHandle);
	     
	        
	        // Pass in the color information
	        aTriangleBuffer[i].position(mColorOffset);
	        GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false,
	                					 mStrideBytes, aTriangleBuffer[i]);
	        
	        GLES20.glEnableVertexAttribArray(mColorHandle);
	        
	        Matrix.setRotateM(mRotXMatrix[i], 0, this.mAngleY, -1.0f, 0.0f, 0.0f); /* NEW STUFF*/
    		Matrix.setRotateM(mRotYMatrix[i], 0, this.mAngleX, 0.0f, 1.0f, 0.0f);

    		float tempMatrix[][] = new float[triangleVert.length][16]; /* NEW STUFF*/
    		Matrix.multiplyMM(tempMatrix[i], 0, mRotYMatrix[i], 0, mRotXMatrix[i], 0);
    		Matrix.multiplyMM(mModelMatrix[i], 0, mScaleMatrix[i], 0, tempMatrix[i], 0);

			// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
	        // (which currently contains model * view).
	        Matrix.multiplyMM(mMVPMatrix[i], 0, mViewMatrix[i], 0, mModelMatrix[i], 0);
	        
	        // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
	        // (which now contains model * view * projection).
	        Matrix.multiplyMM(mMVPMatrix[i], 0, mProjectionMatrix[i], 0, mMVPMatrix[i], 0);
	        
	        GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mMVPMatrix[i], 0);   

	        // Draw the triangle
	        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
	        
	        // Disable vertex array
	        GLES20.glDisableVertexAttribArray(mPositionHandle);
    	}
    }

}