/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * 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.badlogic.gdx.graphics;

import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
import java.util.Map;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.glutils.IndexArray;
import com.badlogic.gdx.graphics.glutils.IndexBufferObject;
import com.badlogic.gdx.graphics.glutils.IndexBufferObjectSubData;
import com.badlogic.gdx.graphics.glutils.IndexData;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.graphics.glutils.VertexArray;
import com.badlogic.gdx.graphics.glutils.VertexBufferObject;
import com.badlogic.gdx.graphics.glutils.VertexBufferObjectSubData;
import com.badlogic.gdx.graphics.glutils.VertexData;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;

/** <p>
 * A Mesh holds vertices composed of attributes specified by a {@link VertexAttributes} instance. The vertices are held either in
 * VRAM in form of vertex buffer objects or in RAM in form of vertex arrays. The former variant is more performant and is prefered
 * over vertex arrays if hardware supports it.
 * </p>
 * 
 * <p>
 * Meshes are automatically managed. If the OpenGL context is lost all vertex buffer objects get invalidated and must be reloaded
 * when the context is recreated. This only happens on Android when a user switches to another application or receives an incoming
 * call. A managed Mesh will be reloaded automagically so you don't have to do this manually.
 * </p>
 * 
 * <p>
 * A Mesh consists of vertices and optionally indices which specify which vertices define a triangle. Each vertex is composed of
 * attributes such as position, normal, color or texture coordinate. Note that not all of this attributes must be given, except
 * for position which is non-optional. Each attribute has an alias which is used when rendering a Mesh in OpenGL ES 2.0. The alias
 * is used to bind a specific vertex attribute to a shader attribute. The shader source and the alias of the attribute must match
 * exactly for this to work. For OpenGL ES 1.x rendering this aliases are irrelevant.
 * </p>
 * 
 * <p>
 * Meshes can be used with either OpenGL ES 1.x or OpenGL ES 2.0.
 * </p>
 * 
 * @author mzechner, Dave Clayton <contact@redskyforge.com> */
public class Mesh implements Disposable {
	public enum VertexDataType {
		VertexArray, VertexBufferObject, VertexBufferObjectSubData,
	}

	/** list of all meshes **/
	static final Map<Application, Array<Mesh>> meshes = new HashMap<Application, Array<Mesh>>();

	/** used for benchmarking **/
	public static boolean forceVBO = false;

	final VertexData vertices;
	final IndexData indices;
	boolean autoBind = true;
	final boolean isVertexArray;

	/** Creates a new Mesh with the given attributes.
	 * 
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param maxVertices the maximum number of vertices this mesh can hold
	 * @param maxIndices the maximum number of indices this mesh can hold
	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
	 *           normal or texture coordinate */
	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
		if (Gdx.gl20 != null || Gdx.gl11 != null || Mesh.forceVBO) {
			vertices = new VertexBufferObject(isStatic, maxVertices, attributes);
			indices = new IndexBufferObject(isStatic, maxIndices);
			isVertexArray = false;
		} else {
			vertices = new VertexArray(maxVertices, attributes);
			indices = new IndexArray(maxIndices);
			isVertexArray = true;
		}

		addManagedMesh(Gdx.app, this);
	}

	/** Creates a new Mesh with the given attributes.
	 * 
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param maxVertices the maximum number of vertices this mesh can hold
	 * @param maxIndices the maximum number of indices this mesh can hold
	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
	 *           normal or texture coordinate */
	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttributes attributes) {
		if (Gdx.gl20 != null || Gdx.gl11 != null || Mesh.forceVBO) {
			vertices = new VertexBufferObject(isStatic, maxVertices, attributes);
			indices = new IndexBufferObject(isStatic, maxIndices);
			isVertexArray = false;
		} else {
			vertices = new VertexArray(maxVertices, attributes);
			indices = new IndexArray(maxIndices);
			isVertexArray = true;
		}

		addManagedMesh(Gdx.app, this);
	}

	/** by jw:
	 * Creates a new Mesh with the given attributes. 
	 * Adds extra optimizations for dynamic (frequently modified) meshes.
	 * 
	 * @param staticVertices whether vertices of this mesh are static or not. Allows for internal optimizations.
	 * @param staticIndices whether indices of this mesh are static or not. Allows for internal optimizations.
	 * @param maxVertices the maximum number of vertices this mesh can hold
	 * @param maxIndices the maximum number of indices this mesh can hold
	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
	 *           normal or texture coordinate 
	 *           
	 * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com>           
	 **/
	public Mesh (boolean staticVertices, boolean staticIndices, int maxVertices, int maxIndices, VertexAttributes attributes) {
		if (Gdx.gl20 != null || Gdx.gl11 != null || Mesh.forceVBO) {
			
			// buffers do not update when initialized with ..ObjectSubData classes
			/*if (staticVertices) 
				vertices = new VertexBufferObject(staticVertices, maxVertices, attributes);
			else 
				vertices = new VertexBufferObjectSubData(staticVertices, maxVertices, attributes);	// when updating vertices - updates buffer instead recreating it

			if (staticIndices) 
				indices = new IndexBufferObject(staticIndices, maxIndices);
			else 
				indices = new IndexBufferObjectSubData(staticIndices, maxIndices);	// when updating indices - updates buffer instead recreating it
			*/
			
			vertices = new VertexBufferObject(staticVertices, maxVertices, attributes);
			indices = new IndexBufferObject(staticIndices, maxIndices);
			isVertexArray = false;
		} else {
			vertices = new VertexArray(maxVertices, attributes);
			indices = new IndexArray(maxIndices);
			isVertexArray = true;
		}

		addManagedMesh(Gdx.app, this);
	}
	
	/** Creates a new Mesh with the given attributes. This is an expert method with no error checking. Use at your own risk.
	 * 
	 * @param type the {@link VertexDataType} to be used, VBO or VA.
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param maxVertices the maximum number of vertices this mesh can hold
	 * @param maxIndices the maximum number of indices this mesh can hold
	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
	 *           normal or texture coordinate */
	public Mesh (VertexDataType type, boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
// if (type == VertexDataType.VertexArray && Gdx.graphics.isGL20Available()) type = VertexDataType.VertexBufferObject;

		if (type == VertexDataType.VertexBufferObject || Mesh.forceVBO) {
			vertices = new VertexBufferObject(isStatic, maxVertices, attributes);
			indices = new IndexBufferObject(isStatic, maxIndices);
			isVertexArray = false;
		} else if (type == VertexDataType.VertexBufferObjectSubData) {
			vertices = new VertexBufferObjectSubData(isStatic, maxVertices, attributes);
			indices = new IndexBufferObjectSubData(isStatic, maxIndices);
			isVertexArray = false;
		} else {
			vertices = new VertexArray(maxVertices, attributes);
			indices = new IndexArray(maxIndices);
			isVertexArray = true;
		}
		addManagedMesh(Gdx.app, this);
	}
	
	/**
	 * Create a new Mesh that is a combination of transformations of the supplied base mesh.
	 * Not all primitive types, like line strip and triangle strip, can be combined.
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param transformations the transformations to apply to the meshes 
	 * @return the combined mesh
	 */
	public static Mesh create(boolean isStatic, final Mesh base, final Matrix4[] transformations) {
		final VertexAttribute posAttr = base.getVertexAttribute(Usage.Position);
		final int offset = posAttr.offset / 4;
		final int numComponents = posAttr.numComponents;
		final int numVertices = base.getNumVertices();
		final int vertexSize = base.getVertexSize() / 4;
		final int baseSize = numVertices * vertexSize;
		final int numIndices = base.getNumIndices();
		
		final float vertices[] = new float[numVertices * vertexSize * transformations.length];
		final short indices[] = new short[numIndices * transformations.length];
		
		base.getIndices(indices);
		
		for (int i = 0; i < transformations.length; i++) {
			base.getVertices(0, baseSize, vertices, baseSize * i);
			transform(transformations[i], vertices, vertexSize, offset, numComponents, numVertices * i, numVertices);
			if (i > 0)
			for (int j = 0; j < numIndices; j++)
				indices[(numIndices * i) + j] = (short)(indices[j] + (numVertices * i)); 
		}
		
		final Mesh result = new Mesh(isStatic, vertices.length/vertexSize, indices.length, base.getVertexAttributes());
		result.setVertices(vertices);
		result.setIndices(indices);
		return result;
	}
	
	/**
	 * Create a new Mesh that is a combination of the supplied meshes. The meshes must have the same VertexAttributes signature.
	 * Not all primitive types, like line strip and triangle strip, can be combined.
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param meshes the meshes to combine
	 * @return the combined mesh
	 */
	public static Mesh create(boolean isStatic, final Mesh[] meshes) {
		return create(isStatic, meshes, null);
	}

	/**
	 * Create a new Mesh that is a combination of the supplied meshes. The meshes must have the same VertexAttributes signature.
	 * If transformations is supplied, it must have the same length as meshes. 
	 * Not all primitive types, like line strip and triangle strip, can be combined.
	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
	 * @param meshes the meshes to combine
	 * @param transformations the transformations to apply to the meshes 
	 * @return the combined mesh
	 */
	public static Mesh create(boolean isStatic, final Mesh[] meshes, final Matrix4[] transformations) {
		if (transformations != null && transformations.length < meshes.length)
			throw new IllegalArgumentException("Not enough transformations specified");
		final VertexAttributes attributes = meshes[0].getVertexAttributes();
		int vertCount = meshes[0].getNumVertices();
		int idxCount = meshes[0].getNumIndices();
		for (int i = 1; i < meshes.length; i++) {
			if (!meshes[i].getVertexAttributes().equals(attributes))
				throw new IllegalArgumentException("Inconsistent VertexAttributes");
			vertCount += meshes[i].getNumVertices();
			idxCount += meshes[i].getNumIndices();
		}
		final VertexAttribute posAttr = meshes[0].getVertexAttribute(Usage.Position);
		final int offset = posAttr.offset / 4;
		final int numComponents = posAttr.numComponents;
		final int vertexSize = attributes.vertexSize / 4;
		
		final float vertices[] = new float[vertCount * vertexSize];
		final short indices[] = new short[idxCount];
		
		meshes[0].getVertices(vertices);
		meshes[0].getIndices(indices);
		
		int voffset = meshes[0].getNumVertices() * vertexSize;
		int ioffset = meshes[0].getNumIndices();
		for (int i = 1; i < meshes.length; i++) {
			final Mesh mesh = meshes[i];
			final int vsize = mesh.getNumVertices() * vertexSize;
			final int isize = mesh.getNumIndices();
			mesh.getVertices(0, vsize, vertices, voffset);
			if (transformations != null)
				transform(transformations[i], vertices, vertexSize, offset, numComponents, voffset / vertexSize, vsize / vertexSize);
			mesh.getIndices(indices, ioffset);
			for (int j = 0; j < isize; j++)
				indices[ioffset+j] = (short)(indices[ioffset+j] + voffset);
			voffset += vsize;
			ioffset += isize;
		}
		
		final Mesh result = new Mesh(isStatic, vertices.length/vertexSize, indices.length, attributes);
		result.setVertices(vertices);
		result.setIndices(indices);
		return result;
	}

	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
	 * 
	 * @param vertices the vertices.
	 * @return the mesh for invocation chaining.*/
	public Mesh setVertices (float[] vertices) {
		this.vertices.setVertices(vertices, 0, vertices.length);
		
		return this;
	}

	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
	 * 
	 * @param vertices the vertices.
	 * @param offset the offset into the vertices array
	 * @param count the number of floats to use 
	 * @return the mesh for invocation chaining.*/
	public Mesh setVertices (float[] vertices, int offset, int count) {
		this.vertices.setVertices(vertices, offset, count);
		
		return this;
	}

	/** Copies the vertices from the Mesh to the float array. The float array must be large enough to hold all the Mesh's vertices.
	 * @param vertices the array to copy the vertices to */
	public void getVertices (float[] vertices) {
		getVertices(0, -1, vertices);
	}
	
	/** Copies the the remaining vertices from the Mesh to the float array. The float array must be large enough to hold the remaining vertices.
	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
	 * @param vertices the array to copy the vertices to */
	public void getVertices (int srcOffset, float[] vertices) {
		getVertices(srcOffset, -1, vertices);
	}

	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold count vertices.
	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
	 * @param count the amount of floats to copy
	 * @param vertices the array to copy the vertices to */
	public void getVertices (int srcOffset, int count, float[] vertices) {
		getVertices(srcOffset, count, vertices, 0);
	}
	
	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold destOffset+count vertices.
	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
	 * @param count the amount of floats to copy
	 * @param vertices the array to copy the vertices to
	 * @param destOffset the offset (in floats) in the vertices array to start copying */
	public void getVertices (int srcOffset, int count, float[] vertices, int destOffset) {
		// TODO: Perhaps this method should be vertexSize aware??
		final int max = getNumVertices() * getVertexSize() / 4;
		if (count == -1) {
			count = max - srcOffset;
			if (count > vertices.length - destOffset)
				count = vertices.length - destOffset;
		}
		if (srcOffset < 0 || count <= 0 || (srcOffset + count) > max || destOffset < 0 || destOffset >= vertices.length)
			throw new IndexOutOfBoundsException();
		if ((vertices.length - destOffset) < count)
			throw new IllegalArgumentException("not enough room in vertices array, has " + vertices.length + " floats, needs " + count);
		int pos = getVerticesBuffer().position();
		getVerticesBuffer().position(srcOffset);
		getVerticesBuffer().get(vertices, destOffset, count);
		getVerticesBuffer().position(pos);
	}

	/** Sets the indices of this Mesh
	 * 
	 * @param indices the indices
	 * @return the mesh for invocation chaining. */
	public Mesh setIndices (short[] indices) {
		this.indices.setIndices(indices, 0, indices.length);
		
		return this;
	}

	/** Sets the indices of this Mesh.
	 * 
	 * @param indices the indices
	 * @param offset the offset into the indices array
	 * @param count the number of indices to copy
	 * @return the mesh for invocation chaining. */
	public Mesh setIndices (short[] indices, int offset, int count) {
		this.indices.setIndices(indices, offset, count);
		
		return this;
	}

	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold all the Mesh's indices.
	 * @param indices the array to copy the indices to */
	public void getIndices (short[] indices) {
		getIndices(indices, 0);
	}

	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold destOffset + all the Mesh's indices.
	 * @param indices the array to copy the indices to 
	 * @param destOffset the offset in the indices array to start copying */
	public void getIndices(short[] indices, int destOffset) {
		if ((indices.length - destOffset) < getNumIndices())
			throw new IllegalArgumentException("not enough room in indices array, has " + indices.length + " floats, needs "
				+ getNumIndices());
		int pos = getIndicesBuffer().position();
		getIndicesBuffer().position(0);
		getIndicesBuffer().get(indices, destOffset, getNumIndices());
		getIndicesBuffer().position(pos);
	}

	/** @return the number of defined indices */
	public int getNumIndices () {
		return indices.getNumIndices();
	}

	/** @return the number of defined vertices */
	public int getNumVertices () {
		return vertices.getNumVertices();
	}

	/** @return the maximum number of vertices this mesh can hold */
	public int getMaxVertices () {
		return vertices.getNumMaxVertices();
	}

	/** @return the maximum number of indices this mesh can hold */
	public int getMaxIndices () {
		return indices.getNumMaxIndices();
	}

	/** @return the size of a single vertex in bytes */
	public int getVertexSize () {
		return vertices.getAttributes().vertexSize;
	}

	/** Sets whether to bind the underlying {@link VertexArray} or {@link VertexBufferObject} automatically on a call to one of the
	 * {@link #render(int)} methods or not. Usually you want to use autobind. Manual binding is an expert functionality. There is a
	 * driver bug on the MSM720xa chips that will fuck up memory if you manipulate the vertices and indices of a Mesh multiple
	 * times while it is bound. Keep this in mind.
	 * 
	 * @param autoBind whether to autobind meshes. */
	public void setAutoBind (boolean autoBind) {
		this.autoBind = autoBind;
	}

	/** Binds the underlying {@link VertexArray}/{@link VertexBufferObject} and {@link IndexBufferObject} if indices were given. Use
	 * this with OpenGL ES 1.x and when auto-bind is disabled. */
	public void bind () {
		if (Gdx.graphics.isGL20Available()) throw new IllegalStateException("can't use this render method with OpenGL ES 2.0");
		vertices.bind();
		if (!isVertexArray && indices.getNumIndices() > 0) indices.bind();
	}

	/** Unbinds the underlying {@link VertexArray}/{@link VertexBufferObject} and {@link IndexBufferObject} is indices were given.
	 * Use this with OpenGL ES 1.x and when auto-bind is disabled. */
	public void unbind () {
		if (Gdx.graphics.isGL20Available()) throw new IllegalStateException("can't use this render method with OpenGL ES 2.0");
		vertices.unbind();
		if (!isVertexArray && indices.getNumIndices() > 0) indices.unbind();
	}

	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
	 * ES 2.0 and when auto-bind is disabled.
	 * 
	 * @param shader the shader (does not bind the shader) */
	public void bind (final ShaderProgram shader) {
		bind(shader, null);
	}

	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
	 * ES 2.0 and when auto-bind is disabled.
	 * 
	 * @param shader the shader (does not bind the shader) 
	 * @param locations array containing the attribute locations. */
	public void bind (final ShaderProgram shader, final int[] locations) {
		if (!Gdx.graphics.isGL20Available()) throw new IllegalStateException("can't use this render method with OpenGL ES 1.x");

		vertices.bind(shader, locations);
		if (indices.getNumIndices() > 0) indices.bind();
	}

	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
	 * ES 1.x and when auto-bind is disabled.
	 * 
	 * @param shader the shader (does not unbind the shader) */
	public void unbind (final ShaderProgram shader) {
		unbind(shader, null);
	}
	
	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
	 * ES 1.x and when auto-bind is disabled.
	 * 
	 * @param shader the shader (does not unbind the shader)
	 * @param locations array containing the attribute locations. */
	public void unbind (final ShaderProgram shader, final int[] locations) {
		if (!Gdx.graphics.isGL20Available()) {
			throw new IllegalStateException("can't use this render method with OpenGL ES 1.x");
		}

		vertices.unbind(shader, locations);
		if (indices.getNumIndices() > 0) indices.unbind();
	}

	/** <p>
	 * Renders the mesh using the given primitive type. If indices are set for this mesh then getNumIndices() / #vertices per
	 * primitive primitives are rendered. If no indices are set then getNumVertices() / #vertices per primitive are rendered.
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 1.x and will throw an IllegalStateException when OpenGL ES 2.0 is used.
	 * </p>
	 * 
	 * @param primitiveType the primitive type */
	public void render (int primitiveType) {
		render(primitiveType, 0, indices.getNumMaxIndices() > 0 ? getNumIndices() : getNumVertices(), autoBind);
	}

	/** <p>
	 * Renders the mesh using the given primitive type. offset specifies the offset into vertex buffer and is ignored for the index
	 * buffer. Count specifies the number of vertices or indices to use thus count / #vertices per primitive primitives are
	 * rendered.
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 1.x and will throw an IllegalStateException when OpenGL ES 2.0 is used.
	 * </p>
	 * 
	 * @param primitiveType the primitive type
	 * @param offset the offset into the vertex buffer, ignored for indexed rendering
	 * @param count number of vertices or indices to use */
	public void render (int primitiveType, int offset, int count) {
		render (primitiveType, offset, count, autoBind);
	}
	
	/** <p>
	 * Renders the mesh using the given primitive type. offset specifies the offset into vertex buffer and is ignored for the index
	 * buffer. Count specifies the number of vertices or indices to use thus count / #vertices per primitive primitives are
	 * rendered.
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 1.x and will throw an IllegalStateException when OpenGL ES 2.0 is used.
	 * </p>
	 * 
	 * @param primitiveType the primitive type
	 * @param offset the offset into the vertex buffer, ignored for indexed rendering
	 * @param count number of vertices or indices to use
	 * @param autoBind overrides the autoBind member of this Mesh */
	public void render (int primitiveType, int offset, int count, boolean autoBind) {
		if (Gdx.graphics.isGL20Available()) throw new IllegalStateException("can't use this render method with OpenGL ES 2.0");
		if (count == 0) return;
		if (autoBind) bind();

		if (isVertexArray) {
			if (indices.getNumIndices() > 0) {
				ShortBuffer buffer = indices.getBuffer();
				int oldPosition = buffer.position();
				int oldLimit = buffer.limit();
				buffer.position(offset);
				buffer.limit(offset + count);
				Gdx.gl10.glDrawElements(primitiveType, count, GL10.GL_UNSIGNED_SHORT, buffer);
				buffer.position(oldPosition);
				buffer.limit(oldLimit);
			} else
				Gdx.gl10.glDrawArrays(primitiveType, offset, count);
		} else {
			if (indices.getNumIndices() > 0)
				Gdx.gl11.glDrawElements(primitiveType, count, GL10.GL_UNSIGNED_SHORT, offset * 2);
			else
				Gdx.gl11.glDrawArrays(primitiveType, offset, count);
		}

		if (autoBind) unbind();
	}

	/** <p>
	 * Renders the mesh using the given primitive type. If indices are set for this mesh then getNumIndices() / #vertices per
	 * primitive primitives are rendered. If no indices are set then getNumVertices() / #vertices per primitive are rendered.
	 * </p>
	 * 
	 * <p>
	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
	 * </p>
	 * 
	 * <p>
	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
	 * </p>
	 * 
	 * @param primitiveType the primitive type */
	public void render (ShaderProgram shader, int primitiveType) {
		render(shader, primitiveType, 0, indices.getNumMaxIndices() > 0 ? getNumIndices() : getNumVertices(), autoBind);
	}

	/** <p>
	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
	 * #vertices per primitive primitives are rendered.
	 * </p>
	 * 
	 * <p>
	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
	 * </p>
	 * 
	 * <p>
	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
	 * </p>
	 * 
	 * @param shader the shader to be used
	 * @param primitiveType the primitive type
	 * @param offset the offset into the vertex or index buffer
	 * @param count number of vertices or indices to use */
	public void render (ShaderProgram shader, int primitiveType, int offset, int count) {
		render (shader, primitiveType, offset, count, autoBind);
	}
	
	/** <p>
	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
	 * #vertices per primitive primitives are rendered.
	 * </p>
	 * 
	 * <p>
	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
	 * </p>
	 * 
	 * <p>
	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
	 * </p>
	 * 
	 * <p>
	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
	 * </p>
	 * 
	 * @param shader the shader to be used
	 * @param primitiveType the primitive type
	 * @param offset the offset into the vertex or index buffer
	 * @param count number of vertices or indices to use
	 * @param autoBind overrides the autoBind member of this Mesh */
	public void render (ShaderProgram shader, int primitiveType, int offset, int count, boolean autoBind) {
		if (!Gdx.graphics.isGL20Available()) throw new IllegalStateException("can't use this render method with OpenGL ES 1.x");
		if (count == 0) return;

		if (autoBind) bind(shader);

		if (isVertexArray) {
			if (indices.getNumIndices() > 0) {
				ShortBuffer buffer = indices.getBuffer();
				int oldPosition = buffer.position();
				int oldLimit = buffer.limit();
				buffer.position(offset);
				buffer.limit(offset + count);
				Gdx.gl20.glDrawElements(primitiveType, count, GL10.GL_UNSIGNED_SHORT, buffer);
				buffer.position(oldPosition);
				buffer.limit(oldLimit);
			} else {
				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
			}
		} else {
			if (indices.getNumIndices() > 0)
				Gdx.gl20.glDrawElements(primitiveType, count, GL10.GL_UNSIGNED_SHORT, offset * 2);
			else
				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
		}

		if (autoBind) unbind(shader);
	}

	/** Frees all resources associated with this Mesh */
	public void dispose () {
		if (meshes.get(Gdx.app) != null) meshes.get(Gdx.app).removeValue(this, true);
		vertices.dispose();
		indices.dispose();
	}

	/** Returns the first {@link VertexAttribute} having the given {@link Usage}.
	 * 
	 * @param usage the Usage.
	 * @return the VertexAttribute or null if no attribute with that usage was found. */
	public VertexAttribute getVertexAttribute (int usage) {
		VertexAttributes attributes = vertices.getAttributes();
		int len = attributes.size();
		for (int i = 0; i < len; i++)
			if (attributes.get(i).usage == usage) return attributes.get(i);

		return null;
	}

	/** @return the vertex attributes of this Mesh */
	public VertexAttributes getVertexAttributes () {
		return vertices.getAttributes();
	}

	/** @return the backing FloatBuffer holding the vertices. Does not have to be a direct buffer on Android! */
	public FloatBuffer getVerticesBuffer () {
		return vertices.getBuffer();
	}

	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
	 * {@link GdxRuntimeException} is thrown. This method creates a new BoundingBox instance.
	 * 
	 * @return the bounding box. */
	public BoundingBox calculateBoundingBox () {
		BoundingBox bbox = new BoundingBox();
		calculateBoundingBox(bbox);
		return bbox;
	}

	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
	 * {@link GdxRuntimeException} is thrown.
	 * 
	 * @param bbox the bounding box to store the result in. */
	public void calculateBoundingBox (BoundingBox bbox) {
		final int numVertices = getNumVertices();
		if (numVertices == 0) throw new GdxRuntimeException("No vertices defined");

		final FloatBuffer verts = vertices.getBuffer();
		bbox.inf();
		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
		final int offset = posAttrib.offset / 4;
		final int vertexSize = vertices.getAttributes().vertexSize / 4;
		int idx = offset;

		switch (posAttrib.numComponents) {
		case 1:
			for (int i = 0; i < numVertices; i++) {
				bbox.ext(verts.get(idx), 0, 0);
				idx += vertexSize;
			}
			break;
		case 2:
			for (int i = 0; i < numVertices; i++) {
				bbox.ext(verts.get(idx), verts.get(idx + 1), 0);
				idx += vertexSize;
			}
			break;
		case 3:
			for (int i = 0; i < numVertices; i++) {
				bbox.ext(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
				idx += vertexSize;
			}
			break;
		}
	}
	
	/** Calculate the {@link BoundingBox} of the specified part.
	 * @param out the bounding box to store the result in. 
	 * @param offset the start index of the part.
	 * @param count the amount of indices the part contains. 
	 * @return the value specified by out. */
	public BoundingBox calculateBoundingBox(final BoundingBox out, int offset, int count) {
		return extendBoundingBox(out.inf(), offset, count);
	}
	
	/** Calculate the {@link BoundingBox} of the specified part.
	 * @param out the bounding box to store the result in. 
	 * @param offset the start index of the part.
	 * @param count the amount of indices the part contains. 
	 * @return the value specified by out. */
	public BoundingBox calculateBoundingBox(final BoundingBox out, int offset, int count, final Matrix4 transform) {
		return extendBoundingBox(out.inf(), offset, count, transform);
	}

	/** Extends the specified {@link BoundingBox} with the specified part.
	 * @param out the bounding box to store the result in. 
	 * @param offset the start index of the part.
	 * @param count the amount of indices the part contains. 
	 * @return the value specified by out. */
	public BoundingBox extendBoundingBox(final BoundingBox out, int offset, int count) {
		return extendBoundingBox(out, offset, count, null);
	}
	
	private final Vector3 tmpV = new Vector3();
	/** Extends the specified {@link BoundingBox} with the specified part.
	 * @param out the bounding box to store the result in. 
	 * @param offset the start index of the part.
	 * @param count the amount of indices the part contains. 
	 * @return the value specified by out. */
	public BoundingBox extendBoundingBox(final BoundingBox out, int offset, int count, final Matrix4 transform) {
		int numIndices = getNumIndices();
		if (offset < 0 || count < 1 || offset + count > numIndices)
			throw new GdxRuntimeException("Not enough indices");
		
		final FloatBuffer verts = vertices.getBuffer();
		final ShortBuffer index = indices.getBuffer();
		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
		final int posoff = posAttrib.offset / 4;
		final int vertexSize = vertices.getAttributes().vertexSize / 4;
		final int end = offset + count;
		
		switch (posAttrib.numComponents) {
		case 1:
			for (int i = offset; i < end; i++) {
				final int idx = index.get(i) * vertexSize + posoff;
				tmpV.set(verts.get(idx), 0, 0);
				if (transform != null)
					tmpV.mul(transform);
				out.ext(tmpV);
			}
			break;
		case 2:
			for (int i = offset; i < end; i++) {
				final int idx = index.get(i) * vertexSize + posoff;
				tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
				if (transform != null)
					tmpV.mul(transform);
				out.ext(tmpV);
			}
			break;
		case 3:
			for (int i = offset; i < end; i++) {
				final int idx = index.get(i) * vertexSize + posoff;
				tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
				if (transform != null)
					tmpV.mul(transform);
				out.ext(tmpV);
			}
			break;
		}
		return out;
	}

	/** @return the backing shortbuffer holding the indices. Does not have to be a direct buffer on Android! */
	public ShortBuffer getIndicesBuffer () {
		return indices.getBuffer();
	}

	private static void addManagedMesh (Application app, Mesh mesh) {
		Array<Mesh> managedResources = meshes.get(app);
		if (managedResources == null) managedResources = new Array<Mesh>();
		managedResources.add(mesh);
		meshes.put(app, managedResources);
	}

	/** Invalidates all meshes so the next time they are rendered new VBO handles are generated.
	 * @param app */
	public static void invalidateAllMeshes (Application app) {
		Array<Mesh> meshesArray = meshes.get(app);
		if (meshesArray == null) return;
		for (int i = 0; i < meshesArray.size; i++) {
			if (meshesArray.get(i).vertices instanceof VertexBufferObject) {
				((VertexBufferObject)meshesArray.get(i).vertices).invalidate();
			}
			meshesArray.get(i).indices.invalidate();
		}
	}

	/** Will clear the managed mesh cache. I wouldn't use this if i was you :) */
	public static void clearAllMeshes (Application app) {
		meshes.remove(app);
	}

	public static String getManagedStatus () {
		StringBuilder builder = new StringBuilder();
		int i = 0;
		builder.append("Managed meshes/app: { ");
		for (Application app : meshes.keySet()) {
			builder.append(meshes.get(app).size);
			builder.append(" ");
		}
		builder.append("}");
		return builder.toString();
	}

	/** Method to scale the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with care.
	 * It will also create a temporary float[] which will be garbage collected.
	 * 
	 * @param scaleX scale on x
	 * @param scaleY scale on y
	 * @param scaleZ scale on z */
	public void scale (float scaleX, float scaleY, float scaleZ) {
		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
		final int offset = posAttr.offset / 4;
		final int numComponents = posAttr.numComponents;
		final int numVertices = getNumVertices();
		final int vertexSize = getVertexSize() / 4;

		final float[] vertices = new float[numVertices * vertexSize];
		getVertices(vertices);

		int idx = offset;
		switch (numComponents) {
		case 1:
			for (int i = 0; i < numVertices; i++) {
				vertices[idx] *= scaleX;
				idx += vertexSize;
			}
			break;
		case 2:
			for (int i = 0; i < numVertices; i++) {
				vertices[idx] *= scaleX;
				vertices[idx + 1] *= scaleY;
				idx += vertexSize;
			}
			break;
		case 3:
			for (int i = 0; i < numVertices; i++) {
				vertices[idx] *= scaleX;
				vertices[idx + 1] *= scaleY;
				vertices[idx + 2] *= scaleZ;
				idx += vertexSize;
			}
			break;
		}

		setVertices(vertices);
	}
	
	/** 
	 * Method to transform the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with care.
	 * It will also create a temporary float[] which will be garbage collected.
	 * 
	 * @param matrix the transformation matrix */
	public void transform(final Matrix4 matrix) {
		transform(matrix, 0, getNumVertices());
	}
	
	// TODO: Protected for now, because transforming a portion works but still copies all vertices
	protected void transform(final Matrix4 matrix, final int start, final int count) {
		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
		final int offset = posAttr.offset / 4;
		final int vertexSize = getVertexSize() / 4;
		final int numComponents = posAttr.numComponents;
		final int numVertices = getNumVertices();
		
		final float[] vertices = new float[numVertices * vertexSize];
		// TODO: getVertices(vertices, start * vertexSize, count * vertexSize);
		getVertices(0, vertices.length, vertices);
		transform(matrix, vertices, vertexSize, offset, numComponents, start, count);
		setVertices(vertices, 0, vertices.length);
		// TODO: setVertices(start * vertexSize, vertices, 0, vertices.length);
	}
	
	/**
	 * Method to transform the positions in the float array. Normals will be kept as is. This is a potentially slow operation, use with care.
	 * @param matrix the transformation matrix
	 * @param vertices the float array
	 * @param vertexSize the number of floats in each vertex
	 * @param offset the offset within a vertex to the position
	 * @param dimensions the size of the position
	 * @param start the vertex to start with
	 * @param count the amount of vertices to transform
	 */
	public static void transform(final Matrix4 matrix, final float[] vertices, int vertexSize, int offset, int dimensions, int start, int count) {
		if (offset < 0 || dimensions < 1 || (offset + dimensions) > vertexSize)
			throw new IndexOutOfBoundsException();
		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
			throw new IndexOutOfBoundsException("start = "+start+", count = "+count+", vertexSize = "+vertexSize+", length = "+vertices.length);
		
		final Vector3 tmp = new Vector3();
		
		int idx = offset + (start * vertexSize);
		switch(dimensions) {
		case 1:
			for (int i = 0; i < count; i++) {
				tmp.set(vertices[idx], 0, 0).mul(matrix);
				vertices[idx] = tmp.x;
				idx += vertexSize;
			}
			break;
		case 2:
			for (int i = 0; i < count; i++) {
				tmp.set(vertices[idx], vertices[idx + 1], 0).mul(matrix);
				vertices[idx] = tmp.x;
				vertices[idx+1] = tmp.y;
				idx += vertexSize;
			}
			break;
		case 3:
			for (int i = 0; i < count; i++) {
				tmp.set(vertices[idx], vertices[idx + 1], vertices[idx + 2]).mul(matrix);
				vertices[idx] = tmp.x;
				vertices[idx+1] = tmp.y;
				vertices[idx+2] = tmp.z;
				idx += vertexSize;
			}
			break;
		}
	}
	
	/** 
	 * Method to transform the texture coordinates in the mesh. This is a potentially slow operation, use with care.
	 * It will also create a temporary float[] which will be garbage collected.
	 * 
	 * @param matrix the transformation matrix */
	public void transformUV(final Matrix3 matrix) {
		transformUV(matrix, 0, getNumVertices());
	}
	
	// TODO: Protected for now, because transforming a portion works but still copies all vertices
	protected void transformUV(final Matrix3 matrix, final int start, final int count) {
		final VertexAttribute posAttr = getVertexAttribute(Usage.TextureCoordinates);
		final int offset = posAttr.offset / 4;
		final int vertexSize = getVertexSize() / 4;
		final int numVertices = getNumVertices();
		
		final float[] vertices = new float[numVertices * vertexSize];
		// TODO: getVertices(vertices, start * vertexSize, count * vertexSize);
		getVertices(0, vertices.length, vertices);
		transformUV(matrix, vertices, vertexSize, offset, start, count);
		setVertices(vertices, 0, vertices.length);
		// TODO: setVertices(start * vertexSize, vertices, 0, vertices.length);
	}
	
	/**
	 * Method to transform the texture coordinates (UV) in the float array. This is a potentially slow operation, use with care.
	 * @param matrix the transformation matrix
	 * @param vertices the float array
	 * @param vertexSize the number of floats in each vertex
	 * @param offset the offset within a vertex to the texture location
	 * @param start the vertex to start with
	 * @param count the amount of vertices to transform
	 */
	public static void transformUV(final Matrix3 matrix, final float[] vertices, int vertexSize, int offset, int start, int count) {
		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
			throw new IndexOutOfBoundsException("start = "+start+", count = "+count+", vertexSize = "+vertexSize+", length = "+vertices.length);
		
		final Vector2 tmp = new Vector2();
		
		int idx = offset + (start * vertexSize);
		for (int i = 0; i < count; i++) {
			tmp.set(vertices[idx], vertices[idx+1]).mul(matrix);
			vertices[idx] = tmp.x;
			vertices[idx+1] = tmp.y;
			idx += vertexSize;
		}
	}
	
	/** Copies this mesh optionally removing duplicate vertices and/or reducing the amount of attributes.
	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
	 * @param removeDuplicates whether to remove duplicate vertices if possible. Only the vertices specified by usage are checked.
	 * @param usage which attributes (if available) to copy
	 * @return the copy of this mesh
	 */
	public Mesh copy(boolean isStatic, boolean removeDuplicates, final int[] usage) {
		// TODO move this to a copy constructor?
		// TODO duplicate the buffers without double copying the data if possible.
		// TODO perhaps move this code to JNI if it turns out being too slow.
		final int vertexSize = getVertexSize() / 4;
		int numVertices = getNumVertices();
		float[] vertices = new float[numVertices * vertexSize];
		getVertices(0, vertices.length, vertices);
		short[] checks = null;
		VertexAttribute[] attrs = null;
		int newVertexSize = 0;
		if (usage != null) {
			int size = 0;
			int as = 0;
			for (int i = 0; i < usage.length; i++)
				if (getVertexAttribute(usage[i]) != null) {
					size += getVertexAttribute(usage[i]).numComponents;
					as++;
				}
			if (size > 0) {
				attrs = new VertexAttribute[as];
				checks = new short[size];
				int idx = -1;
				int ai = -1;
				for (int i = 0; i < usage.length; i++) {
					VertexAttribute a = getVertexAttribute(usage[i]);
					if (a == null)
						continue;
					for (int j = 0; j < a.numComponents; j++)
						checks[++idx] = (short)(a.offset + j);
					attrs[++ai] = new VertexAttribute(a.usage, a.numComponents, a.alias);
					newVertexSize += a.numComponents;
				}
			}
		}
		if (checks == null) {
			checks = new short[vertexSize];
			for (short i = 0; i < vertexSize; i++)
				checks[i] = i;
			newVertexSize = vertexSize;
		}
		
		int numIndices = getNumIndices();
		short[] indices = null;	
		if (numIndices > 0) {
			indices = new short[numIndices];
			getIndices(indices);
			if (removeDuplicates || newVertexSize != vertexSize) {
				float[] tmp = new float[vertices.length];
				int size = 0;
				for (int i = 0; i < numIndices; i++) {
					final int idx1 = indices[i] * vertexSize;
					short newIndex = -1;
					if (removeDuplicates) {
						for (short j = 0; j < size && newIndex < 0; j++) {
							final int idx2 = j*newVertexSize;
							boolean found = true;
							for (int k = 0; k < checks.length && found; k++) {
								if (tmp[idx2+k] != vertices[idx1+checks[k]])
									found = false;
							}
							if (found)
								newIndex = j;
						}
					}
					if (newIndex > 0)
						indices[i] = newIndex;
					else {
						final int idx = size * newVertexSize;
						for (int j = 0; j < checks.length; j++)
							tmp[idx+j] = vertices[idx1+checks[j]];
						indices[i] = (short)size;
						size++;
					}
				}
				vertices = tmp;
				numVertices = size;
			}
		}
		
		Mesh result;
		if (attrs == null)
			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, getVertexAttributes());
		else
			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, attrs);
		result.setVertices(vertices, 0, numVertices * newVertexSize);
		result.setIndices(indices);
		return result;
	}
	
	/** Copies this mesh.
	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
	 * @return the copy of this mesh
	 */
	public Mesh copy(boolean isStatic) {
		return copy(isStatic, false, null);
	}
}
