/*
 *  Flingbox - An OpenSource physics sandbox for Google's Android
 *  Copyright (C) 2009  Jon Ander Peñalba & Endika Gutiérrez
 *
 *  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 edu.eside.flingbox.graphics;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import edu.eside.flingbox.bodies.Polygon;
import edu.eside.flingbox.math.PolygonUtils;
import edu.eside.flingbox.math.Vector2D;

/**
 * {@link RenderPolygon} handles functions to render {@link Polygon}
 * into OpenGL's space. 
 * Translation and rotation values should be set in {@link AtomicBody}
 * and calculated by physic engine.
 * 
 * This should only be instantiate by {@link Polygon}.
 */
public class RenderPolygon implements RenderBody {
	/** Buffer with vertex, for OpenGL */
	private final FloatBuffer mVertexBuffer;
	/** Buffer with triangulation indexes, for OpenGL */
	private final ShortBuffer mIndexBuffer;
	/** Triangles count */
	private final short mTrianglesCount;
	
	/** Position to draw polygon */
	private final Vector2D mPosition = new Vector2D();
	/** Angle of the polygon */
	private float mAngle = 0f;
	
	/** Stores polygon's color */
	private float[] mColor = new float[] { 0f, 0f, 0f, 1f };

	/**
	 * Default constructor of PolygonRender.
	 * initializes values needed by OpenGL.
	 * 
	 * @param points	Polygon's points, with centroid at 0,0
	 */
	public RenderPolygon(final Vector2D[] points) {
		final int pointsCount = points.length;
		
		/* Fill 2D polygon into 3D space */
		float[] points3D = new float[3 * pointsCount];
		for (int i = 0; i < pointsCount; i++) {
			points3D[3 * i + 0] = points[i].i;		// x
			points3D[3 * i + 1] = points[i].j;		// y
			points3D[3 * i + 2] = 0f;				// z
		}
		
		mVertexBuffer = ByteBuffer // Fill buffers with correspondent vertex
			.allocateDirect(4 * 3 * pointsCount)
			.order(ByteOrder.nativeOrder())
			.asFloatBuffer()
			.put(points3D);
		mVertexBuffer.position(0);
		
		/* Set point drawing order by triangulation */
		short[] triangulationIndexes = PolygonUtils.triangulatePolygon(points);
		mIndexBuffer = ByteBuffer
			.allocateDirect(2 * triangulationIndexes.length)
			.order(ByteOrder.nativeOrder())
			.asShortBuffer()
			.put(triangulationIndexes);
		mIndexBuffer.position(0);
		
		mTrianglesCount = (short) (triangulationIndexes.length / 3);
	}
	
	/**
	 * Sets color of the polygon
	 * 
	 * @param r		Red channel [0, 1]
	 * @param g		Green channel [0, 1]
	 * @param b		Blue channel [0, 1]
	 * @param alpha	Alpha channel, sets object's transparency, 0 for transparent
	 * 		1 for opaque.
	 */
	public void setColor(float r, float g, float b, float alpha) {
		mColor[0] = r;
		mColor[1] = g;
		mColor[2] = b;
		mColor[3] = alpha;
	}
	
	/**
	 * Sets object's position to render 
	 * 
	 * @param position	Point to current position
	 * @param rotation	Rotation angle in radiants
	 */
	public void setPosition(final Vector2D position, final float rotation) {
		/* Copy point. Avoid objects creation */
		mPosition.set(position);
		
		/* Set angle into degrees */
		mAngle = rotation * 360.0f / (2f * (float) Math.PI);
	}
	
	/**
	 * Renderizes Polygon into gl
	 */
	public boolean onRender(GL10 gl) {
		/* Set color */
		gl.glColor4f(mColor[0], mColor[1], mColor[2], mColor[3]);
		
		/* First translate object for it's position */
		gl.glTranslatef(mPosition.i, mPosition.j, 0f);
		/* Then rotate it */
		gl.glRotatef(((int) mAngle) % 360, 0f, 0f, 1.0f);
		try {
			/* Draw it */
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
			gl.glDrawElements(GL10.GL_TRIANGLES, 3 * mTrianglesCount, 
					GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
		} catch (Exception ex) {
			return false; // Body couldn't be rendered
		}
		return true; // Body render succeed
	}
}
