package com.android.draw3d;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.opengles.GL10;

import org.opencv.core.Point3;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;

public class DrawModel {
	private FloatBuffer cube_vertexBuffer;
	private FloatBuffer pyramid_vertexBuffer;
	private FloatBuffer pyramid_colorBuffer;   // Buffer for color-array
	private ByteBuffer pyramid_indexBuffer;    // Buffer for index-array
	private FloatBuffer texBuffer;
	private int numFaces = 6;

	private float[][] cube_colors = { // Colors of the 6 faces
			{ 1.0f, 0.5f, 0.0f, 1.0f }, // 0. orange
			{ 1.0f, 0.0f, 1.0f, 1.0f }, // 1. violet
			{ 0.0f, 1.0f, 0.0f, 1.0f }, // 2. green
			{ 0.0f, 0.0f, 1.0f, 1.0f }, // 3. blue
			{ 1.0f, 0.0f, 0.0f, 1.0f }, // 4. red
			{ 1.0f, 1.0f, 0.0f, 1.0f }, // 5. yellow
			{ 0.0f, 1.0f, 1.0f, 1.0f }, // 6.
			{ 1.0f, 1.0f, 1.0f, 1.0f } // 7. white
	};
	
	private float[] pyramid_colors = { // Colors of the 6 faces
			1.0f, 0.5f, 0.0f, 1.0f , // 0. orange
			1.0f, 0.0f, 1.0f, 1.0f , // 1. violet
			0.0f, 1.0f, 0.0f, 1.0f , // 2. green
			0.0f, 0.0f, 1.0f, 1.0f , // 3. blue
			1.0f, 0.0f, 0.0f, 1.0f , // 4. red
			1.0f, 1.0f, 0.0f, 1.0f , // 5. yellow
			0.0f, 1.0f, 1.0f, 1.0f , // 6.
			1.0f, 1.0f, 1.0f, 1.0f  // 7. white
	};

	private float[] pyramid_vertices = { // 6 vertices of the pyramid in (x,y,z)
			1.0f,  0.0f, 1.0f, // 0. left-middle-back
			0.0f,  1.0f, 1.0f, // 1. right-middle-back
		   -1.0f,  0.0f, 1.0f, // 2. right-middle-front
			0.0f, -1.0f, 1.0f, // 3. left-middle-front
			0.0f,  0.0f, 0.0f, // 4. bottom
			0.0f,  0.0f, 2.0f // 5. top
	};
	
	private byte[] pyramid_indices = { // Vertex indices of the 8 Triangles
		      2, 4, 3,
		      1, 4, 2,
		      0, 4, 1,
		      4, 0, 3,
		      
		      2, 5, 3,
		      1, 5, 2,
		      0, 5, 1,
		      5, 0, 3
		   };
	
	double cub_size = 1;
	double cub_halfsize = cub_size/2;
	public Point3 cub_corner0 = new Point3(-cub_halfsize,-cub_halfsize,0);
	public Point3 cub_corner1 = new Point3(cub_halfsize,-cub_halfsize,0);
	public Point3 cub_corner2 = new Point3(cub_halfsize,cub_halfsize,0);
	public Point3 cub_corner3 = new Point3(-cub_halfsize,cub_halfsize,0);
	public Point3 cub_corner4 = new Point3(-cub_halfsize,-cub_halfsize,-cub_size);
	public Point3 cub_corner5 = new Point3(cub_halfsize,-cub_halfsize,-cub_size);
	public Point3 cub_corner6 = new Point3(cub_halfsize,cub_halfsize,-cub_size);
	public Point3 cub_corner7 = new Point3(-cub_halfsize,cub_halfsize,-cub_size);
	
	public float[] cube_vertices = { // Vertices of the 6 faces
			// FRONT
			(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
			(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
			(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
			(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
			// BACK
			(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
			(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
			(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
			(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
			// LEFT
			(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
			(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
			(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
			(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
			// RIGHT
			(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
			(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
			(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
			(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
			// TOP
			(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
			(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
			(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
			(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
			// BOTTOM
			(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
			(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
			(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
			(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
	};

	float[] texCoords = { // Texture coords for the above face (NEW)
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f, // D. right-top (NEW)
			
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f, // D. right-top (NEW)
			
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f, // D. right-top (NEW)
			
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f, // D. right-top (NEW)
			
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f, // D. right-top (NEW)
			
			0.0f, 1.0f, // A. left-bottom (NEW)
			1.0f, 1.0f, // B. right-bottom (NEW)
			0.0f, 0.0f, // C. left-top (NEW)
			1.0f, 0.0f // D. right-top (NEW)
	};

	int[] textureIDs = new int[1]; // Array for 1 texture-ID (NEW)

	public DrawModel() {
		cube_set(cube_vertices);
		pyramid_set();
	}
	
	public void cube_set(float[] vertices){
		// Setup vertex-array buffer. Vertices in float. An float has 4 bytes
		ByteBuffer vbb_cube = ByteBuffer.allocateDirect(vertices.length * 4);
		vbb_cube.order(ByteOrder.nativeOrder()); // Use native byte order
		cube_vertexBuffer = vbb_cube.asFloatBuffer(); // Convert from byte to float
		cube_vertexBuffer.position(0); // Rewind	
		cube_vertexBuffer.put(vertices); // Copy data into buffer
		cube_vertexBuffer.position(0); // Rewind		
	}
	
	public void cub_translation(float x, float y, float z){
		cub_corner0 = new Point3(cub_halfsize+x,cub_halfsize+y,cub_size+z);
		cub_corner1 = new Point3(-cub_halfsize+x,cub_halfsize+y,cub_size+z);
		cub_corner2 = new Point3(-cub_halfsize+x,-cub_halfsize+y,cub_size+z);
		cub_corner3 = new Point3(cub_halfsize+x,-cub_halfsize+y,cub_size+z);
		cub_corner4 = new Point3(cub_halfsize+x,cub_halfsize+y,0+z);
		cub_corner5 = new Point3(-cub_halfsize+x,cub_halfsize+y,0+z);
		cub_corner6 = new Point3(-cub_halfsize+x,-cub_halfsize+y,0+z);
		cub_corner7 = new Point3(cub_halfsize+x,-cub_halfsize+y,0+z);
		
		float[] new_cube_vertices = { // Vertices of the 6 faces
				// FRONT
				(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
				(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
				(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
				(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
				// BACK
				(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
				(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
				(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
				(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
				// LEFT
				(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
				(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
				(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
				(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
				// RIGHT
				(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
				(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
				(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
				(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
				// TOP
				(float)cub_corner0.x, (float)cub_corner0.y, (float)cub_corner0.z,
				(float)cub_corner4.x, (float)cub_corner4.y, (float)cub_corner4.z,
				(float)cub_corner1.x, (float)cub_corner1.y, (float)cub_corner1.z,
				(float)cub_corner5.x, (float)cub_corner5.y, (float)cub_corner5.z,
				// BOTTOM
				(float)cub_corner2.x, (float)cub_corner2.y, (float)cub_corner2.z,
				(float)cub_corner6.x, (float)cub_corner6.y, (float)cub_corner6.z,
				(float)cub_corner3.x, (float)cub_corner3.y, (float)cub_corner3.z,
				(float)cub_corner7.x, (float)cub_corner7.y, (float)cub_corner7.z,
		};
		
		cube_set(new_cube_vertices);
	}
	
	public void cube_draw(GL10 gl) {
		gl.glFrontFace(GL10.GL_CCW); // Front face in counter-clockwise
										// orientation
		gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
		gl.glCullFace(GL10.GL_BACK); // Cull the back face (don't display)

		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cube_vertexBuffer);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // Enable
																// texture-coords-array
																// (NEW)
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define
		// texture-coords buffer (NEW)

		// Render all the faces
		for (int face = 0; face < numFaces; face++) {
			// Set the color for each of the faces
			//gl.glColor4f(cube_colors[face][0], cube_colors[face][1], cube_colors[face][2], cube_colors[face][3]);
			// Draw the primitive from the vertex-array directly
			gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, face * 4, 4);
		}

		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisable(GL10.GL_CULL_FACE);
	}
	
	public void pyramid_set(){
		// Setup vertex-array buffer. Vertices in float. An float has 4 bytes
		ByteBuffer vbb_pyramid = ByteBuffer.allocateDirect(pyramid_vertices.length * 4);
		vbb_pyramid.order(ByteOrder.nativeOrder()); // Use native byte order
		pyramid_vertexBuffer = vbb_pyramid.asFloatBuffer(); // Convert from byte to float
		pyramid_vertexBuffer.put(pyramid_vertices); // Copy data into buffer
		pyramid_vertexBuffer.position(0); // Rewind
		
				
		pyramid_indexBuffer = ByteBuffer.allocateDirect(pyramid_indices.length);
		pyramid_indexBuffer.put(pyramid_indices);
		pyramid_indexBuffer.position(0);
				
		// Setup color-array buffer. Colors in float. An float has 4 bytes
		ByteBuffer cbb = ByteBuffer.allocateDirect(pyramid_colors.length * 4);
		cbb.order(ByteOrder.nativeOrder());
		pyramid_colorBuffer = cbb.asFloatBuffer();
		pyramid_colorBuffer.put(pyramid_colors);
		pyramid_colorBuffer.position(0);
	}
	
	public void pyramid_draw(GL10 gl) {
	      gl.glFrontFace(GL10.GL_CCW);  // Front face in counter-clockwise orientation
	  
	      // Enable arrays and define their buffers
	      gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	      gl.glVertexPointer(3, GL10.GL_FLOAT, 0, pyramid_vertexBuffer);
	      gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
	      gl.glColorPointer(4, GL10.GL_FLOAT, 0, pyramid_colorBuffer);
	      
	      gl.glDrawElements(GL10.GL_TRIANGLES, pyramid_indices.length, GL10.GL_UNSIGNED_BYTE, pyramid_indexBuffer);
	      
	      gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
	      gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
	   }

	public void loadTexture(GL10 gl, Context context) {
		// Load texture
		ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4);
		tbb.order(ByteOrder.nativeOrder());
		texBuffer = tbb.asFloatBuffer();
		texBuffer.put(texCoords);
		texBuffer.position(0);
				
		gl.glGenTextures(1, textureIDs, 0); // Generate texture-ID array

		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]); // Bind to texture
																// ID
		// Set up texture filters
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

		// Construct an input stream to texture image "res\drawable\nehe.png"
		InputStream istream = context.getResources().openRawResource(R.drawable.crate);
		Bitmap bitmap;
		try {
			// Read and decode input as bitmap
			bitmap = BitmapFactory.decodeStream(istream);
		} finally {
			try {
				istream.close();
			} catch (IOException e) {
			}
		}

		// Build Texture from loaded bitmap for the currently-bind texture ID
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
	}
}
