package simogl.geometry;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.media.opengl.GL;

import simogl.collision.BoundingBox;

/**
 * This class represents a mesh. The mesh uses the vertex aray technology.
 * @author Frank Bruns
 *
 */
public class MeshVA extends AbstractMesh
{
	ByteBuffer vertices = null;
	ByteBuffer normals = null;
	ByteBuffer texCoords = null;
	
	private int BUFFER_SIZE = 0;
	
	public MeshVA()
	{
		
	}
	
	/**
	 * This method creates the vertex array for the mesh.
	 * It allocates the required space for the byte buffer.
	 * It is essentiell to call this method after every face has been
	 * added to the mesh object.
	 *
	 */
	public void createVertexArray()
	{
		// 2 * number of faces = number of triangles
		// 3 * number of triangles = number of vertices (points)
		// 3 * number of vertices = number of coordinates
		// 4 * number coordinates = number of bytes for the values 
		this.BUFFER_SIZE = this.getFaces().size()*2*3*3*4;		
		this.vertices = ByteBuffer.allocateDirect(this.BUFFER_SIZE);
		this.vertices.order(ByteOrder.nativeOrder());
		
		
		// 2 * number of faces = number of Triangles
		// 3 * number of triangles = number of vertices (points)
		// 3 * number of vertices = number of normals
		// 1 * number of normals per vertex
		// 4 * number coordinates = number of bytes for the float-values
		this.BUFFER_SIZE = this.faces.size()*2*3*3*1*4;
		this.normals = ByteBuffer.allocateDirect(this.BUFFER_SIZE);
		this.normals.order(ByteOrder.nativeOrder());
		
		
		// 2 * number of faces = number of Triangles
		// 3 * number of triangles = number of vertices (points)
		// 2 * number of vertices = number of texture coordinates
		// 4 * number tex coordinates = number of bytes for the float-values
		this.BUFFER_SIZE = this.faces.size()*2*3*2*4;	
		this.texCoords = ByteBuffer.allocateDirect(this.BUFFER_SIZE);
		this.texCoords.order(ByteOrder.nativeOrder());
		
		for(Face face : this.faces)
		{			
			// first triangle
			this.vertices.putFloat(face.getTriangle1().getVertex1().getX());
			this.vertices.putFloat(face.getTriangle1().getVertex1().getY());
			this.vertices.putFloat(face.getTriangle1().getVertex1().getZ());
			this.normals.putFloat(face.getTriangle1().getNormal().getX());
			this.normals.putFloat(face.getTriangle1().getNormal().getY());
			this.normals.putFloat(face.getTriangle1().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex1().getS());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex1().getT());
			
			this.vertices.putFloat(face.getTriangle1().getVertex2().getX());
			this.vertices.putFloat(face.getTriangle1().getVertex2().getY());
			this.vertices.putFloat(face.getTriangle1().getVertex2().getZ());
			this.normals.putFloat(face.getTriangle1().getNormal().getX());
			this.normals.putFloat(face.getTriangle1().getNormal().getY());
			this.normals.putFloat(face.getTriangle1().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex2().getS());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex2().getT());
			
			this.vertices.putFloat(face.getTriangle1().getVertex3().getX());
			this.vertices.putFloat(face.getTriangle1().getVertex3().getY());
			this.vertices.putFloat(face.getTriangle1().getVertex3().getZ());
			this.normals.putFloat(face.getTriangle1().getNormal().getX());
			this.normals.putFloat(face.getTriangle1().getNormal().getY());
			this.normals.putFloat(face.getTriangle1().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex3().getS());
			this.texCoords.putFloat(face.getTriangle1().getTexCoordVertex3().getT());
			
			
			// second triangle
			this.vertices.putFloat(face.getTriangle2().getVertex1().getX());
			this.vertices.putFloat(face.getTriangle2().getVertex1().getY());
			this.vertices.putFloat(face.getTriangle2().getVertex1().getZ());
			this.normals.putFloat(face.getTriangle2().getNormal().getX());
			this.normals.putFloat(face.getTriangle2().getNormal().getY());
			this.normals.putFloat(face.getTriangle2().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex1().getS());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex1().getT());
			
			this.vertices.putFloat(face.getTriangle2().getVertex2().getX());
			this.vertices.putFloat(face.getTriangle2().getVertex2().getY());
			this.vertices.putFloat(face.getTriangle2().getVertex2().getZ());
			this.normals.putFloat(face.getTriangle2().getNormal().getX());
			this.normals.putFloat(face.getTriangle2().getNormal().getY());
			this.normals.putFloat(face.getTriangle2().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex2().getS());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex2().getT());
			
			this.vertices.putFloat(face.getTriangle2().getVertex3().getX());
			this.vertices.putFloat(face.getTriangle2().getVertex3().getY());
			this.vertices.putFloat(face.getTriangle2().getVertex3().getZ());
			this.normals.putFloat(face.getTriangle2().getNormal().getX());
			this.normals.putFloat(face.getTriangle2().getNormal().getY());
			this.normals.putFloat(face.getTriangle2().getNormal().getZ());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex3().getS());
			this.texCoords.putFloat(face.getTriangle2().getTexCoordVertex3().getT());
		}
		
		this.vertices.rewind();
		this.normals.rewind();
		this.texCoords.rewind();
		
		// create bounding box for the mesh
		this.boundingBox = new BoundingBox(this);
	}
	
	@Override
	public void deleteMesh(GL gl)
	{
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void draw(GL gl)
	{		
		this.enableRenderOptions(gl);		
		this.specializedDraw(gl);		
		this.disableRenderOptions(gl);
	}
	
	/**
	 * Helper method to extract the specialised things just fitting to
	 * drawing of vertex arrays. This method is also used by shadow drawing.
	 * @param gl
	 */
	@Override
	public void specializedDraw(GL gl)
	{
		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
		gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
		gl.glVertexPointer(3,GL.GL_FLOAT,0,this.vertices);
		gl.glNormalPointer(GL.GL_FLOAT,0,this.normals);
		gl.glTexCoordPointer(2,GL.GL_FLOAT,0,this.texCoords);

		gl.glPushMatrix();				
			this.performTransformations(gl);					
			gl.glDrawArrays(GL.GL_TRIANGLES,0,this.faces.size()*2*3);
		gl.glPopMatrix();
		
		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL.GL_NORMAL_ARRAY);	
		gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY);
	}
}

