/***********************************************************************
 * mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
 *  
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ***********************************************************************/
package org.mt4j.components.visibleComponents.shapes.mesh;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;

import org.mt4j.components.bounds.BoundingSphere;
import org.mt4j.components.bounds.IBoundingShape;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.math.BezierVertex;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.ToolsBuffers;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLTexture;

import processing.core.PApplet;
import processing.core.PGraphics;
import processing.opengl.PGraphicsOpenGL;

/**
 * A mesh class for drawing triangle meshes.
 * 
 * @author Christopher Ruff
 */
public class MTTriangleMesh extends AbstractShape{
	
	/** The triangles. */
	private Triangle[] triangles; 
	
	/** The draw normals. */
	private boolean drawNormals;
	
	//FIXME EXPERIMENTAL
	/** The outline contours. */
	private List<Vertex[]> outlineContours;
	
	/** The outline buffers. */
	private List<FloatBuffer> outlineBuffers;
	
	private boolean calculateDefaultNormals = true; //has to be initialized here, else not considered in first setGeomInfo()..
	
	/**
	 * Creates a new triangle mesh.
	 * 
	 * <p><strong>Important</strong>:  This mesh expects triangles geometry!
	 * <br><li>An unindexed geometry's vertex array
	 * should contain pairs of three vertices.
	 * <br><li> An indexed geometry should contain pairs of three indices.
	 * <br>
	 * 
	 * @param pApplet the applet
	 * @param geometryInfo the geometry info
	 */
	public MTTriangleMesh(PApplet pApplet, GeometryInfo geometryInfo) {
		this(pApplet, geometryInfo, true);
	}
	
	
	/**
	 * Creates a new triangle mesh.
	 * 
	 * <p><strong>Important</strong>:  This mesh expects triangles geometry!
	 * <br><li>An unindexed geometry's vertex array
	 * should contain pairs of three vertices.
	 * <br><li> An indexed geometry should contain pairs of three indices.
	 * <br>
	 * 
	 * @param pApplet the applet
	 * @param geometryInfo the geometry info
	 * @param calculateDefaultNormals sets if default normals for lightning should be 
	 * calculated if the geometry doesent containe them yet - WE ARE ONLY REQUIRED TO CALCULATE NORMALS
	 * IF THE MESH IS TO BE USED WITH LIGHTNING!
	 */
	public MTTriangleMesh(PApplet pApplet, GeometryInfo geometryInfo, boolean calculateDefaultNormals) {
		super(geometryInfo, pApplet);
		
		this.calculateDefaultNormals = calculateDefaultNormals;
		
		//FIXME EXPERIMENTAL
		this.outlineContours = new ArrayList<Vertex[]>();
		this.outlineBuffers = new ArrayList<FloatBuffer>();
		
		//Some Settings
		this.setFillDrawMode(GL.GL_TRIANGLES);
		this.setName("unnamed triangle mesh");
		this.drawNormals = false;
		this.setNoStroke(true);
		
		this.setBoundsBehaviour(AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK);
//		this.setBoundsPickingBehaviour(AbstractShape.BOUNDS_ONLY_GEOMETRY_CHECK);
//		this.setBoundsPickingBehaviour(AbstractShape.BOUNDS_ONLY_CHECK);
		
		//FIXME we dont create triangles or normals here because we do so in the overriden method setGeometryInfo() 
		//which gets called in the super (abstractShape) constructor anyway
		//-> else createTris() and createDefaultNormals would be called twice!!
		//Create triangles from all the vertices (pairs of 3) or from the indices for internal use
//		this.createTriangles(this.getGeometryInfo()); 
		//Create normals for use with OpenGL lighting, 
		//but only if the geomInfo doesent have norms and createDefaultNormals = true
//		this.createDefaultNormals(geometryInfo);
	}

	
	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setGeometryInfo(org.mt4j.components.visibleComponents.GeometryInfo)
	 */
	@Override
	public void setGeometryInfo(GeometryInfo geometryInfo) {
		//KEEP IN MIND THAT THIS IS CALLED IN ABSTRACTSHAPE CONSTRUCTOR!
		//Create tris before setGeometryInfo, because in setGeometryInfo, 
		//defaultBounds are calced (may depend on triangles)
		this.createTriangles(geometryInfo);
		super.setGeometryInfo(geometryInfo);
		this.createDefaultNormals(geometryInfo);
	}



	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setVertices(org.mt4j.util.math.Vertex[])
	 */
	@Override
	public void setVertices(Vertex[] vertices) {
		//create tris before setVertices, because in setVerts, defaultBounds are calced (depend on triangles)
		this.createTriangles(new GeometryInfo(this.getRenderer(), vertices, this.getGeometryInfo().getNormals(), //TODO why not reconstruct() when geometryinfo already there?
				this.getGeometryInfo().getIndices()));
		super.setVertices(vertices);
		this.createDefaultNormals(this.getGeometryInfo());
	}
	
	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#computeDefaultBounds()
	 */
	@Override
	protected IBoundingShape computeDefaultBounds() {
		return new BoundingSphere(this);
//		return new OrientedBoundingBox(this));
	}

	

	/**
	 * Checks if default normals should be calculated if a
	 * new geometryInfo or new vertices are set on this mesh.
	 * 
	 * @return true, if is creates the default normals
	 */
	public boolean isCalculateDefaultNormals() {
		return calculateDefaultNormals;
	}


	/**
	 * Sets if default normals should be calculated if a
	 * new geometryInfo or new vertices are set on this mesh.
	 * 
	 * @param calculateDefaultNormals the new creates the default normals
	 */
	public void setCalculateDefaultNormals(boolean calculateDefaultNormals) {
		this.calculateDefaultNormals = calculateDefaultNormals;
	}
	
	/**
	 * Calculates default normals for this mesh if the mesh's geometryInfo doesent
	 * yet contain normals.
 	 * Creates face or vertex normals - <br>
	 * Face normals if the geometry isnt indexed and Vertex normals are created if the geometry
	 * IS indexed.
	 */
	public void calculateDefaultNormals(){
		boolean oldVal = this.isCalculateDefaultNormals();
		this.setCalculateDefaultNormals(true);
		this.createDefaultNormals(this.getGeometryInfo());
		this.setCalculateDefaultNormals(oldVal);
	}


	/**
	 * Creates face or vertex normals if the geometryinfo doesent already contain normals and 
	 * if isCalculateDefaultNormals() is true.
	 * Creates face normals if the geometry isnt indexed. Vertex normals are created if geometry
	 * IS indexed.
	 * 
	 * @param geometryInfo the geometry info
	 */
	private void createDefaultNormals(GeometryInfo geometryInfo){
		if (!this.getGeometryInfo().isContainsNormals() && this.isCalculateDefaultNormals()){
//			System.out.println("MTTriangleMesh object: \"" + this + "\" -> Create default normals.");
			
			//Create and set the face normals
			if (!geometryInfo.isIndexed()){
				Vector3D[] normals = this.getFaceOrVertexNormals();
				this.getGeometryInfo().setNormals(normals, true, this.isUseVBOs());
			}else{
				//System.err.println("Triangle mesh geometry contains no normals and is indexed -> To create normals use the MeshNormalGenerator!");
				Vector3D[] normals = this.getFaceOrVertexNormals();
				this.getGeometryInfo().setNormals(normals, true, this.isUseVBOs());
			}
		}
	}
	
	

	/**
	 * Create triangles from the mesh information.
	 * <br>If the geometryInfo is indexed, one triangle is created per 3 indices.
	 * <br>If the geometry inst indexes, one triangle per 3 vertices is creates.
	 * 
	 * @param geom the geom
	 */
	private void createTriangles(GeometryInfo geom){
		Vertex[] vertices = geom.getVertices();
		
		//TODO geometryInfo.getDrawMode()? bei tristrip m�sste ein triangle erzeugt werden f�r jedes neue vertex!?
		ArrayList<Triangle> tris = new ArrayList<Triangle>();
		
		if (geom.isIndexed()){
			//System.out.println("MTTriangleMesh object: \"" + this.getName() + "\" Debug-> Supplied geometry is INDEXED");
			
			int[] indices = geom.getIndices();
			if (indices.length % 3 != 0){
				System.err.println("WARNING: the indices of the indexed mesh geometry:\"" + this.getName() + "\" arent dividable by 3 => probably no TRIANGLES indices provided!");
			}
			
			for (int i = 0; i < indices.length/3; i++) {
				int vertIndex0 = indices[i*3];
				int vertIndex1 = indices[i*3+1];
				int vertIndex2 = indices[i*3+2];
				
				Vertex v0 = vertices[vertIndex0];
				Vertex v1 = vertices[vertIndex1];
				Vertex v2 = vertices[vertIndex2];
				
				tris.add(new Triangle(v0, v1, v2, vertIndex0, vertIndex1, vertIndex2));
			}
		}else{
			//System.out.println("MTTriangleMesh object: \"" + this.getName() + "\" Debug-> Supplied geometry is NOT INDEXED");
			if (vertices.length % 3 != 0){
				System.err.println("WARNING: the vertices of the mesh geometry:\"" + this.getName() + "\" arent dividable by 3 => probably no TRIANGLES array provided!");
			}
			
			//geht nur bei vertices/3 = ganze zahl (vertices sind dreiecke!)
			for (int i = 0; i < vertices.length/3; i++) {
				int vertIndex0 = i*3;
				int vertIndex1 = i*3+1;
				int vertIndex2 = i*3+2;
				
				Vertex v0 = vertices[vertIndex0];
				Vertex v1 = vertices[vertIndex1];
				Vertex v2 = vertices[vertIndex2];
				
				tris.add(new Triangle(v0, v1, v2, vertIndex0, vertIndex1, vertIndex2));
			}
		}
		this.triangles = (Triangle[])tris.toArray(new Triangle[tris.size()]);
		
//		System.out.println("MTTriangleMesh object: \"" + this + "\" Debug-> Triangles created: " + this.triangles.length);
	}
	
	


	/**
	 * Creates an array of normals, 1 for every vertex or index.
	 * , so every drawn vertex will have a normal information.
	 * <br>If the geometry is indexed, smooth interpolated normals
	 * are generated, else face normals are generated.
	 * 
	 * @return the face or vertex normals
	 * 
	 * the normals
	 */
	private Vector3D[] getFaceOrVertexNormals(){
		Vector3D[] normals = new Vector3D[this.triangles.length*3];
		
		GeometryInfo geom = this.getGeometryInfo();
		if (geom.isIndexed()){
			//Create smooth vertex normals, smoothed across all neighbors
			int[] indices = geom.getIndices();
			normals = new Vector3D[geom.getVertices().length];
			for (int i = 0; i < indices.length/3; i++) {
				if (normals[indices[i*3]] == null){
					normals[indices[i*3]] = triangles[i].getNormalLocal().getCopy();
				}else{
					normals[indices[i*3]].addLocal(triangles[i].getNormalLocal());
				}
				if (normals[indices[i*3+1]] == null){
					normals[indices[i*3+1]] = triangles[i].getNormalLocal().getCopy();
				}else{
					normals[indices[i*3+1]].addLocal(triangles[i].getNormalLocal());
				}
				if (normals[indices[i*3+2]] == null){
					normals[indices[i*3+2]] = triangles[i].getNormalLocal().getCopy();
				}else{
					normals[indices[i*3+2]].addLocal(triangles[i].getNormalLocal());
				}
			}
			for (int i = 0; i < normals.length; i++) {
				Vector3D n = normals[i];
				if (n == null){
					n = new Vector3D(0,0,1);
				}else{
					n.normalizeLocal();
				}
			}
		}else
		{
			//Create face normals for unindexed geometry
			for (int i = 0; i < this.triangles.length; i++) {
				Triangle tri = this.triangles[i];
				normals[i*3] 	= tri.getNormalLocal();
				normals[i*3+1] 	= tri.getNormalLocal();
				normals[i*3+2] 	= tri.getNormalLocal();
			}
		}
//		System.out.println("MTTriangleMesh Debug-> Normals created: " + normals.length);
		return normals;
	}
	

	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#getCenterPointLocal()
	 */
	@Override
	public Vector3D getCenterPointLocal() {
		if (this.hasBounds()){
			return this.getBounds().getCenterPointLocal();
		}else{
			BoundingSphere tempBounds = new BoundingSphere(this);
			return tempBounds.getCenterPointLocal();
		}
	}
	
	
	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#isGeometryContainsPointLocal(org.mt4j.util.math.Vector3D)
	 */
	@Override
	public boolean isGeometryContainsPointLocal(Vector3D testPoint) {
		// Point to local space - already done!
//		testPoint.transform(this.getGlobalInverseMatrix());
		
		//Send ray from the test point in x, y, and z direction 
		//and count the intersections (this is not really sufficient!)
		Ray ray0 = new Ray(new Vector3D(testPoint), new Vector3D(1,0,0));
		Ray ray1 = new Ray(new Vector3D(testPoint), new Vector3D(0,1,0));
		Ray ray2 = new Ray(new Vector3D(testPoint), new Vector3D(0,0,1));
		
		int i0 = this.getNumIntersections(ray0);
		int i1 = this.getNumIntersections(ray1);
		int i2 = this.getNumIntersections(ray2);
		
		/*
		System.out.println("I0:" + i0);
		System.out.println("I1:" + i1);
		System.out.println("I2:" + i2);
		*/
		
		//Check if intersection count is odd -> inside
		return ((i0 & 1 ) != 0) 
			&& ((i1 & 1 ) != 0)
			&& ((i2 & 1 ) != 0);
	}

	
	/**
	 * Check how often the ray intersects with the meshes triangles.
	 * 
	 * @param ray the ray
	 * 
	 * @return the number of intersecitons
	 */
	private int getNumIntersections(Ray ray){
		//Evtl auch bbox pr�fen?
		int intersectionsFound = 0;
		
		//Save intersections to check duplicates because at an edge
		//bewteeen 2 triangles both intersections are counted but
		//we still can say we�re on the inside
		boolean checkThoroughly = true;
		ArrayList<Vector3D> intersections = new ArrayList<Vector3D>();
		
		for (int i = 0; i < triangles.length; i++) {
			Triangle tri = triangles[i];
			Vector3D intersectionPoint = tri.getRayTriangleIntersection(ray);
			boolean sameAlreadyEncountered = false;

			if (intersectionPoint != null){
				if (checkThoroughly){
					for(Vector3D v: intersections){
						if (v.equalsVectorWithTolerance(intersectionPoint, ToolsMath.ZERO_TOLERANCE)){
							sameAlreadyEncountered = true;
						}
					}
				}
				if (!sameAlreadyEncountered){
					intersections.add(intersectionPoint);
					intersectionsFound++;	
				}
			}
		}
		intersections = null; //Clean
		return intersectionsFound;
	}
	

	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#getGeometryIntersectionLocal(org.mt4j.util.math.Ray)
	 */
	@Override
	public Vector3D getGeometryIntersectionLocal(Ray ray){
		float distance = Float.MAX_VALUE;
		Vector3D returnVect = null;
		for (int i = 0; i < triangles.length; i++) {
			Triangle tri = triangles[i];
			Vector3D intersectionPoint = tri.getRayTriangleIntersection(ray);
			if (intersectionPoint != null){
				float objDistance = intersectionPoint.getSubtracted(ray.getRayStartPoint()).length();
				
				//It is accurate to go through all triangles and use the closest intersection
				//but slower as just returning the first..
				if (objDistance <= distance ){
					distance 	= objDistance;
					returnVect 	= intersectionPoint;
				}
			}
		}
		/*
		if (returnVect != null){
			System.out.println("Picked mesh: " + this.getName());
		}
		*/
		return returnVect;
	}
	
	
	/**
	 * Gets the triangle count.
	 * 
	 * @return the triangle count
	 * 
	 * number of triangles in this mesh
	 */
	public int getTriangleCount(){
		return triangles.length;
	}
	
	/**
	 * The triangles of this triangle mesh.
	 * 
	 * @return the triangles
	 */
	public Triangle[] getTriangles(){
		if (this.triangles == null){
			this.createTriangles(this.getGeometryInfo());
			return this.triangles;
		}else{
			return this.triangles;
		}
	}
	
	
	
	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.AbstractVisibleComponent#drawComponent(processing.core.PGraphics)
	 */
	@Override
	public void drawComponent(PGraphics g) {
		PApplet pa = this.getRenderer();
		
		if (this.isUseDirectGL()){
			GL gl=((PGraphicsOpenGL)g).beginGL();
				this.drawComponent(gl);
			((PGraphicsOpenGL)this.getRenderer().g).endGL();
		}else{ //Draw with pure proccessing...
			pa.strokeWeight(this.getStrokeWeight());
			
//			if (ConstantsAndSettings.getInstance().isOpenGlMode()){
				if (this.isDrawSmooth()) 
					pa.smooth();
				else 			
					pa.noSmooth();
//			}
			
			//NOTE: if noFill() and noStroke()->absolutely nothing will be drawn-even when texture is set
			if (this.isNoFill())	
				pa.noFill();
			else{
				MTColor fillColor = this.getFillColor();
				pa.fill(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
			}
			
			if (this.isNoStroke()) 	
				pa.noStroke();
			else{
				MTColor strokeColor = this.getStrokeColor();
				pa.stroke(strokeColor.getR(), strokeColor.getG(), strokeColor.getB(), strokeColor.getAlpha());
			}
			
			//Set the tint values 
			MTColor fillColor = this.getFillColor();
			pa.tint(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());	

			//handles the drawing of the vertices with the texture coordinates
			//try doing a smoothed poly outline with opengl
//			if (
////					ConstantsAndSettings.getInstance().isOpenGlMode()  &&
//				this.isDrawSmooth() &&
//				!this.isNoStroke()
//			){
//				pa.noStroke();
//				pa.noSmooth();
//				
//				//draw insided of polygon, without smooth or stroke
//				this.drawWithProcessing(pa, this.getVerticesObjSpace(), PApplet.TRIANGLES, true); 
//
//				pa.smooth();
//				pa.noFill(); 
//				pa.stroke(this.getStrokeRed(), this.getStrokeGreen(), this.getStrokeBlue(), this.getStrokeAlpha());
//
//				// DRAW SMOOTHED THE OUTLINE SHAPE OF THE POLYGON WIHTOUT FILL OR TEXTURE
////				drawWithProcessing(pa); 
//				
//				for (Vertex[] outline : this.outlineContours){
//					this.drawWithProcessing(pa, outline, PApplet.LINE, false);
//				}
//			}else{
			if (!this.isNoStroke()){
				pa.noFill(); 
				MTColor strokeColor = this.getStrokeColor();
				pa.stroke(strokeColor.getR(), strokeColor.getG(), strokeColor.getB(), strokeColor.getAlpha());
				pa.strokeWeight(2);
				
				if (this.isDrawSmooth())
					pa.smooth();
				
				for (Vertex[] outline : this.outlineContours){
					this.drawWithProcessing(pa, outline, PApplet.POLYGON, false);
				}
			}

			if (!this.isNoFill()){
				pa.noStroke();
				pa.noSmooth();
				pa.fill(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
				this.drawWithProcessing(pa, this.getVerticesLocal(), PApplet.TRIANGLES, true);
			}


//			}//end if gl and smooth

			//reSet the tint values to defaults 
			pa.tint(255, 255, 255, 255);

			if (/*ConstantsAndSettings.getInstance().isOpenGlMode() && */
					this.isDrawSmooth())
				pa.noSmooth(); //because of tesselation bug
		}

		if (drawNormals)
			this.drawNormals();
	}

	
	/**
	 * Can be used to draw if the gl context has already been set up and is ready to use.
	 * (processingApplet.beginGL() has been called etc..)
	 * 
	 * @param gl the gl
	 */
	public void drawComponent(GL gl) {
		if (this.isUseDisplayList()){
			int[] displayLists = this.getGeometryInfo().getDisplayListIDs();
			if (!this.isNoFill()){
				gl.glCallList(displayLists[0]);
			}
			if (!this.isNoStroke()){
				if (this.outlineContours != null){
					gl.glCallList(displayLists[1]);
				}
			}
		}else{
			if (this.isNoFill() && this.isNoStroke()){
				return;
			}else{
				this.drawPureGl(gl);	
			}
		}
	}


	/**
	 * loops through all the vertices of the polygon
	 * and uses processings "vertex()" command to set their position
	 * and texture.
	 * 
	 * @param p the p
	 * @param vertices the vertices
	 * @param drawMode the draw mode
	 * @param useTexture the use texture
	 */
	private void drawWithProcessing(PApplet p, Vertex[] vertices, int drawMode, boolean useTexture){
		p.beginShape(drawMode); 
		if (this.getTexture() != null && this.isTextureEnabled() && useTexture){
			p.texture(this.getTexture());
			p.textureMode(this.getTextureMode());
		}
		if (this.getGeometryInfo().isIndexed()){
			int[] indices =  this.getGeometryInfo().getIndices();
			for (int i = 0; i < indices.length; i++) {
				int index = indices[i];
				drawP5Vertex(p, vertices[index], useTexture);
			}
		}
		else{
			for (int i = 0; i < vertices.length; i++) {
				drawP5Vertex(p, vertices[i], useTexture);
			}
		}
		p.endShape();
	}
	
	/**
	 * Draw p5 vertex.
	 * 
	 * @param p the p
	 * @param v the v
	 * @param useTexture the use texture
	 */
	private void drawP5Vertex(PApplet p, Vertex v, boolean useTexture){
		if (this.isTextureEnabled() && useTexture){
			p.vertex(v.x, v.y, v.z, v.getTexCoordU(), v.getTexCoordV());
		}else{
			if (v.getType() == Vector3D.BEZIERVERTEX){
				BezierVertex b = (BezierVertex)v;
				p.bezierVertex(
						b.getFirstCtrlPoint().x,  b.getFirstCtrlPoint().y,  b.getFirstCtrlPoint().z, 
						b.getSecondCtrlPoint().x, b.getSecondCtrlPoint().y, b.getSecondCtrlPoint().z, 
						b.x, b.y, b.z  );
			}
			else{
				p.vertex(v.x, v.y, v.z);
			}
		}
	}


	/**
	 * Draws the face normals of the mesh.
	 */
	private void drawNormals(){
		PApplet r = this.getRenderer();
//		Vector3D[] normals = this.getGeometryInfo().getNormals();
		r.stroke(255, 0, 0);
		r.strokeWeight(0.5f);
		
		for (int i = 0; i < triangles.length; i++) {
			Triangle t = triangles[i];
			r.pushMatrix();
			Vector3D centerPoint = t.getCenterPointLocal();
			r.translate(centerPoint.x, centerPoint.y, centerPoint.z);
			r.scale(-2,-2,-2);
			
			r.line(0, 0, 0,  t.getNormalLocal().x, t.getNormalLocal().y, t.getNormalLocal().z);
			r.popMatrix();
		}
//		for (int i = 0; i < normals.length; i++) {
//			Vector3D vector3D = normals[i];
//		}
	}
	
	
	/**
	 * Draws the mesh with ogl functions.
	 * 
	 * @param gl the gl
	 */
	private void drawPureGl(GL gl){
//		/*
		//Get display array/buffer pointers
		FloatBuffer tbuff 			= this.getGeometryInfo().getTexBuff();
		FloatBuffer vertBuff 		= this.getGeometryInfo().getVertBuff();
		FloatBuffer colorBuff 		= this.getGeometryInfo().getColorBuff();
		IntBuffer indexBuff 		= this.getGeometryInfo().getIndexBuff(); //null if not indexed
		
		//Enable Pointers, set vertex array pointer
		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL.GL_COLOR_ARRAY);
		
		if (this.isUseVBOs()){//Vertices
			gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOVerticesName());
			gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
		}else{
			gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertBuff);
		}
		
		//Default texture target
		int textureTarget = GL.GL_TEXTURE_2D;
		
		/////// DRAW SHAPE ///////
		if (!this.isNoFill()){ 
			boolean textureDrawn = false;
			if (this.isTextureEnabled()
				&& this.getTexture() != null 
				&& this.getTexture() instanceof GLTexture) //Bad for performance?
			{
				GLTexture tex = (GLTexture)this.getTexture();
				textureTarget = tex.getTextureTarget();
				
				//tells opengl which texture to reference in following calls from now on!
				//the first parameter is eigher GL.GL_TEXTURE_2D or ..1D
				gl.glEnable(textureTarget);
				gl.glBindTexture(textureTarget, tex.getTextureID());
				gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
				
				if (this.isUseVBOs()){//Texture
					gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOTextureName());
					gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
				}else{
					gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, tbuff);
				}
				textureDrawn = true;
			}
			
			// Normals
			if (this.getGeometryInfo().isContainsNormals()){
				gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
				if (this.isUseVBOs()){
					gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBONormalsName());
					gl.glNormalPointer(GL.GL_FLOAT, 0, 0); 
				}else{
					gl.glNormalPointer(GL.GL_FLOAT, 0, this.getGeometryInfo().getNormalsBuff());
				}
			}
			
			if (this.isUseVBOs()){//Color
				gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOColorName());
				gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
			}else{
				gl.glColorPointer(4, GL.GL_FLOAT, 0, colorBuff);
			}
			
			//DRAW with drawElements if geometry is indexed, else draw with drawArrays!
			if (this.getGeometryInfo().isIndexed()){
				gl.glDrawElements(this.getFillDrawMode(), indexBuff.capacity(), GL.GL_UNSIGNED_INT, indexBuff); //limit() oder capacity()??

				/*
				int error = gl.glGetError();
				if (error != GL.GL_NO_ERROR){
					System.out.println("GL Error: " + error);
				}else{
					System.out.println("No gl error.");
				}
				*/
			}else{
				gl.glDrawArrays(this.getFillDrawMode(), 0, vertBuff.capacity()/3);
			}
			
			if (this.getGeometryInfo().isContainsNormals()){
				gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
			}

			if (textureDrawn){
				gl.glBindTexture(textureTarget, 0);//Unbind texture
				gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY);
				gl.glDisable(textureTarget); //weiter nach unten?
			}
		}
		
		boolean outlineDrawn = false;
		////////// DRAW OUTLINE ////////
		if (!this.isNoStroke()
				&& this.outlineBuffers != null //FIXME EXPERIMENT
				&& this.outlineContours != null
		){ 
			outlineDrawn = true;
			
//			FloatBuffer strokeColBuff = this.getStrokeColBuff();
//			if (this.isUseVBOs()){
//				gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getVBOStrokeColorName());
//				gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
//				
//				gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);//Experimental 
//			}else{
//				gl.glColorPointer(4, GL.GL_FLOAT, 0, strokeColBuff);
//			}
			
			//Turn on smooth outlines
//			if (this.isDrawSmooth())
//				gl.glEnable(GL.GL_LINE_SMOOTH);
			//FIXME TEST
			Tools3D.setLineSmoothEnabled(gl, true);
			
//			/*
			//SET LINE STIPPLE
				short lineStipple = this.getLineStipple();
				if (lineStipple != 0){
					gl.glLineStipple(1, lineStipple);
					gl.glEnable(GL.GL_LINE_STIPPLE);
				}
//			*/
			
			if (this.getStrokeWeight() > 0)
				gl.glLineWidth(this.getStrokeWeight());
			
			//Dont use geometryinfo strokecolor buffer because its useless in a trianglemesh 
			//instead we use a single, simple stroke color and custom outlines, if provided 
			gl.glDisableClientState(GL.GL_COLOR_ARRAY); //disable color buffer use
			gl.glColor4f(strokeR, strokeG, strokeB, strokeA);
			
			//Always use just buffes and drawarrays instead of vbos..too complicated for a simple outline..
			for(FloatBuffer outlineBuffer : this.outlineBuffers){ //FIXME EXPERIMENTAL
				gl.glVertexPointer(3, GL.GL_FLOAT, 0, outlineBuffer); 
//				gl.glDrawArrays(GL.GL_LINES, 0, outlineBuffer.capacity()/3);
//				gl.glDrawArrays(GL.GL_LINE_LOOP, 0, outlineBuffer.capacity()/3);
				gl.glDrawArrays(GL.GL_LINE_STRIP, 0, outlineBuffer.capacity()/3);
			}
			
			/*
			//DRAW mesh outline
			//Draw with drawElements if geometry is indexed, else draw with drawArrays!
			if (this.getGeometryInfo().isIndexed()){
				gl.glDrawElements(GL.GL_LINES, indexBuff.limit(), GL.GL_UNSIGNED_INT, indexBuff);
//				gl.glDrawElements(this.getFillDrawMode(), indexBuff.capacity(), GL.GL_UNSIGNED_INT, indexBuff);
			}else{
				gl.glDrawArrays(GL.GL_LINES, 0, vertBuff.capacity()/3);
			}
			*/
			//RESET LINE STIPPLE
			if (lineStipple != 0){
				gl.glDisable(GL.GL_LINE_STIPPLE);
			}
//			if (this.isDrawSmooth())
//				gl.glDisable(GL.GL_LINE_SMOOTH);
			//FIXME TEST
			Tools3D.setLineSmoothEnabled(gl, false);
		}
		
		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
		if (!outlineDrawn){ 
			gl.glDisableClientState(GL.GL_COLOR_ARRAY); //If outline drawn we disabled color_array earlier
		}
		
		if (this.isUseVBOs()){
			gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
			gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
		}
//		*/

	}


	/**
	 * Checks if is draw normals.
	 * 
	 * @return true, if is draw normals
	 */
	public boolean isDrawNormals() {
		return drawNormals;
	}
	
	/**
	 * Sets the draw normals.
	 * 
	 * @param drawNormals the new draw normals
	 */
	public void setDrawNormals(boolean drawNormals) {
		this.drawNormals = drawNormals;
	}


	@Override
	protected void destroyComponent() {
		this.triangles = null;

//		if (!this.outlineBuffers.isEmpty()){
//			outline = getGeometryInfo().getDisplayListIDs()[1];
//		}
		
		this.outlineBuffers.clear();
	}
	
	
	@Override
	protected void destroyDisplayLists() {
		super.destroyDisplayLists();
		//System.out.println("Destroying mesh display lists: " + this);
	}


	/**
	 * Gets the outline contours.
	 * 
	 * @return the outline contours
	 */
	public List<Vertex[]> getOutlineContours() {
		return this.outlineContours;
	}
	
	/**
	 * Creates the outline buffers.
	 */
	private void createOutlineBuffers(){
		outlineBuffers.clear();
		for (Vertex[] outline : this.outlineContours){
			outlineBuffers.add(ToolsBuffers.generateVertexBuffer(outline));
		}
	}
	
	
	/** The stroke r. */
	private float strokeR = 1;
	
	/** The stroke g. */
	private float strokeG = 1;
	
	/** The stroke b. */
	private float strokeB = 1;
	
	/** The stroke a. */
	private float strokeA = 1;

	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setStrokeColor(org.mt4j.util.MTColor)
	 */
	@Override
	public void setStrokeColor(MTColor strokeColor) {
		super.setStrokeColor(strokeColor);
		this.strokeR = strokeColor.getR()/255f;
		this.strokeG = strokeColor.getG()/255f;
		this.strokeB = strokeColor.getB()/255f;
		this.strokeA = strokeColor.getAlpha()/255f;
	}


	/**
	 * Sets the outline contours.
	 * <br><strong>NOTE: </strong> To draw the outlines,
	 * the default setting of noStroke has to be changed to false!
	 * 
	 * @param contours the new outline contours
	 */
	public void setOutlineContours(List<Vertex[]> contours) {
		this.outlineContours = contours;
		this.createOutlineBuffers();
		
		if (this.isUseDisplayList()){
			int[] ids = this.getGeometryInfo().getDisplayListIDs();
			//Delete default outline display list, not really usable in a mesh.
			if (MT4jSettings.getInstance().isOpenGlMode()){
				GL gl =((PGraphicsOpenGL)this.getRenderer().g).gl;
				if (ids[1] != -1){
					gl.glDeleteLists(ids[1], 1);
				}
			}
			if (this.outlineContours != null){
				ids[1] = generateContoursDisplayList();
				this.getGeometryInfo().setDisplayListIDs(ids);
			}
		}
	}
	
	
	/* (non-Javadoc)
	 * @see org.mt4j.components.visibleComponents.shapes.AbstractShape#generateDisplayLists()
	 */
	@Override
	public void generateDisplayLists(){
		super.generateDisplayLists();
		int[] ids = this.getGeometryInfo().getDisplayListIDs();
		//Delete default outline display list, not really usable in a mesh. To draw all triangles outlines..
		//TODO nicht optimal, da erst eine displaylist erstellt wird, die gleich wieder gel�scht wird..
		if (MT4jSettings.getInstance().isOpenGlMode()
			&& this.outlineContours != null
		){
			if (ids[1] != -1){
				GL gl =((PGraphicsOpenGL)this.getRenderer().g).gl;
				gl.glDeleteLists(ids[1], 1);
			}
			//Create outline display list from manually set outline contours if available.
			ids[1] = this.generateContoursDisplayList();
			this.getGeometryInfo().setDisplayListIDs(ids);
		}
	}
	
	/**
	 * Gens a displaylists of the outline contours (for svgs important for example, not so much for 3d objects..)
	 * 
	 * @return the int
	 */
	private int generateContoursDisplayList(){
		GL gl=((PGraphicsOpenGL)this.getRenderer().g).gl;
		int listId = gl.glGenLists(1);
		if (listId == 0){
			System.err.println("Failed to create display list");
			return 0;
		}

		gl.glNewList(listId, GL.GL_COMPILE);
		
//		if (this.isDrawSmooth()){
//			gl.glEnable(GL.GL_LINE_SMOOTH);
//		}
		//FIXME TEST
		Tools3D.setLineSmoothEnabled(gl, true);

		gl.glLineWidth(this.getStrokeWeight());
		FloatBuffer strokeColBuff = this.getGeometryInfo().getStrokeColBuff(); 
		gl.glColor4d (strokeColBuff.get(0), strokeColBuff.get(1), strokeColBuff.get(2), strokeColBuff.get(3));
		for (Vertex[] varr : this.outlineContours){
			gl.glBegin(GL.GL_LINE_STRIP);
			for(Vertex v : varr){
				gl.glVertex3f(v.x, v.y, v.z);
			}
			gl.glEnd();
		}
		
		//FIXME TEST
		Tools3D.setLineSmoothEnabled(gl, false);
		
		gl.glEndList();
		return listId;
	}

	

	
	/*
	private Vector3D[] generateNonIndexedNormals(){
		GeometryInfo geom 			= this.getGeometryInfo();
		NonIndexedNormalGenerator n = new NonIndexedNormalGenerator();
		
		Vertex[] vertices = geom.getVerticesLocal();
		float[] verts = new float[vertices.length*3];
		
		for (int i = 0; i < verts.length/3; i++) {
			verts[i*3] 		= vertices[i].x;
			verts[i*3+1] 	= vertices[i].y;
			verts[i*3+2] 	= vertices[i].z;
		}
		
		float[] normals = n.generateNormals(verts , geom.getIndices(), 90);
		Vector3D[] normalVecs = new Vector3D[normals.length/3];
		
		for (int i = 0; i < normalVecs.length; i++) {
			normalVecs[i] = new Vector3D(normals[i*3], normals[i*3+1],normals[i*3+2]);
		}
		return normalVecs;
	}
	*/

}
