package edu.uprm.walsaip.vte.core.renderer;

import static java.lang.System.out;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.media.opengl.GL;

import edu.uprm.walsaip.vte.core.datatypes.Vector;
import edu.uprm.walsaip.vte.core.datatypes.Vertex;
import edu.uprm.walsaip.vte.core.datatypes.terrain.TerrainData;
import edu.uprm.walsaip.vte.core.util.BufferUtils;



/**
 * 
 * @author Ricardo Veguilla (veguilla@ece.uprm.edu)
 * 
 */
public class VertexBufferData {// implements Data {

	public final static int VERTEX_ID = 0;

	public final static int COLOR_ID = 1;

	public final static int NORMAL_ID = 2;

	public final static int INDEX_ID = 3;

	private IntBuffer idBuffer;

	private FloatBuffer vertexBuffer;

	private FloatBuffer normalBuffer;

	private FloatBuffer colorBuffer;

	private IntBuffer indexBuffer;


	private TerrainData terrainData;
	private TerrainData imageData;
	private int primitiveType;

	private int width;
	private int height;

	private float scale;

	private float resolution = 1.0f;

	//private DataFilter filter;
	//private TerrainFileLoader terrainLoader;
	//private ImageFileLoader imageLoader;
	
	/**
	 * @return Returns the size.
	 */
	// public Dimension getSize() {
	// return size;
	// }
	/**
	 * @param size
	 *            The size to set.
	 */
	// public void setSize(Dimension size) {
	// this.size = size;
	// }
	/**
	 * 
	 *
	public VertexBufferData(List<Data> dataList, DataFilter filter,
			int primitiveType) {
		super();
		this.filter = filter;
		this.dataList = dataList;
		this.primitiveType = primitiveType;
		init();
	}
*/
	public VertexBufferData(TerrainData terrainData, TerrainData imageData,int primitiveType) {
		super();
		
		this.terrainData = terrainData;
		this.imageData = imageData;
		this.primitiveType = primitiveType;
		init();
	}

	public void init() {
	
		//this.vertexBuffer = terrainData.getAsFloatBuffer();
		
		this.width = terrainData.getWidth();
		this.height = terrainData.getHeight();
		this.indexBuffer = calculateIndices(primitiveType, width,height);
		this.normalBuffer = calculateNormals(primitiveType, width,height, vertexBuffer,	indexBuffer);
//		if (imageData != null)
//			this.colorBuffer = calculateColors(primitiveType,imageData,indexBuffer);
		
	}

	/**
	 * @return Returns the scale.
	 */
	public float getScale() {
		return scale;
	}

	/**
	 * @param scale
	 *            The scale to set.
	 */
	public void setScale(float scale) {
		this.scale = scale;
	}

	public FloatBuffer getVertexBuffer() {
		if (vertexBuffer != null)
			return vertexBuffer.asReadOnlyBuffer();
		return null;
	}

	public FloatBuffer getColorBuffer() {
		if (colorBuffer != null)
			return colorBuffer.asReadOnlyBuffer();
		else
			return null;
	}

	public FloatBuffer getNormalBuffer() {
		if (normalBuffer != null)
			return normalBuffer.asReadOnlyBuffer();
		else
			return null;
	}

	public IntBuffer getIndexBuffer() {
		if (indexBuffer != null)
			return indexBuffer.asReadOnlyBuffer();
		else
			return null;
	}

	public IntBuffer getIdBuffer() {
		if (idBuffer == null)
			idBuffer = BufferUtils.allocateIntBuffer(4);
		return idBuffer;
	}

	public void setIdBuffer(IntBuffer idBuffer) {
		this.idBuffer = idBuffer;
	}

	public float getResolution() {
		return resolution;
	}

	public void setResolution(float resolution) {
		this.resolution = resolution;
	}

	/**
	 * @return Returns the numIndex.
	 */
	public int getNumIndices() {
		return indexBuffer.capacity();
	}

	private IntBuffer calculateIndices(int primitiveType, int width,int height) {
		out.printf("Calculating indices...");

		int offset_h = (int) (1.0 / resolution);
		int offset_w = (int) (1.0 / resolution);
		int vertexPerPixel = 6;
		if (primitiveType == GL.GL_QUADS)
			vertexPerPixel = 4;
		int numIndices = ((width - 1) / offset_w)
				* ((height - 1) / offset_h) * vertexPerPixel;
		IntBuffer indexBuffer = BufferUtils.allocateIntBuffer(numIndices);
		int pt1, pt2, pt3, pt4;

		for (int h = 0; h < height - offset_h; h += offset_h) {
			for (int w = 0; w < width - offset_w; w += offset_w) {

				pt1 = w + (width * h);
				pt2 = (w + offset_w) + (width * h);
				pt3 = (w + offset_w) + (width * (h + offset_h));
				pt4 = w + (width * (h + offset_h));

				switch (primitiveType) {

				case GL.GL_QUADS:

					// out.println("["+pt1+","+pt2+","+pt3 +","+pt4 +"]");
					indexBuffer.put(pt1);
					indexBuffer.put(pt2);
					indexBuffer.put(pt3);
					indexBuffer.put(pt4);
					break;
				case GL.GL_TRIANGLES:
					indexBuffer.put(pt4);
					indexBuffer.put(pt1);
					indexBuffer.put(pt2);

					indexBuffer.put(pt2);
					indexBuffer.put(pt3);
					indexBuffer.put(pt4);
					break;
				}
			}
			// out.println();
		}
		indexBuffer.rewind();
		out.printf("done.\n");
		return indexBuffer;
	}

	private FloatBuffer calculateNormals(int primitiveType, int width,int height,
			FloatBuffer vertexBuffer, IntBuffer indexBuffer) {
		out.printf("Calculating normals...");

		int numNormals = width * height * 3;
		int numIndices = indexBuffer.capacity();
		FloatBuffer normalBuffer = BufferUtils.allocateFloatBuffer(numNormals);

		float[] normal = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] tempNormal = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] vertex1 = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] vertex2 = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] vertex3 = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] edge1 = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		float[] edge2 = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
		int vertexIndex1, vertexIndex2, vertexIndex3;
		int normalIndex1, normalIndex2, normalIndex3, normalIndex4;
		int increament = 3;
		if (primitiveType == GL.GL_QUADS)
			increament = 4;

		int baseIndex1, baseIndex2, baseIndex3, baseIndex4;
		for (int i = 0; i < numIndices; i += increament) {

			switch (primitiveType) {

			case GL.GL_QUADS:
				baseIndex1 = indexBuffer.get();
				baseIndex2 = indexBuffer.get();
				baseIndex3 = indexBuffer.get();
				baseIndex4 = indexBuffer.get();

				vertexIndex1 = baseIndex1 * 4;
				vertexIndex2 = baseIndex2 * 4;
				vertexIndex3 = baseIndex3 * 4;

				normalIndex1 = baseIndex1 * 3;
				normalIndex2 = baseIndex2 * 3;
				normalIndex3 = baseIndex3 * 3;
				normalIndex4 = baseIndex4 * 3;

				vertexBuffer.position(vertexIndex1);
				vertexBuffer.get(vertex1, 0, 3);
				vertexBuffer.position(vertexIndex2);
				vertexBuffer.get(vertex2, 0, 3);
				vertexBuffer.position(vertexIndex3);
				vertexBuffer.get(vertex3, 0, 3);

				Vertex.subtract(vertex1, vertex2, edge1);
				Vertex.subtract(vertex3, vertex2, edge2);

				Vector.crossProduct(edge1, edge2, tempNormal);
				Vector.normalize(tempNormal, normal);

				normalBuffer.position(normalIndex1);
				normalBuffer.put(normal, 0, 3);
				normalBuffer.position(normalIndex2);
				normalBuffer.put(normal, 0, 3);
				normalBuffer.position(normalIndex3);
				normalBuffer.put(normal, 0, 3);
				normalBuffer.position(normalIndex4);
				normalBuffer.put(normal, 0, 3);

				break;
			case GL.GL_TRIANGLES:
				baseIndex1 = indexBuffer.get();
				baseIndex2 = indexBuffer.get();
				baseIndex3 = indexBuffer.get();

				vertexIndex1 = baseIndex1 * 4;
				vertexIndex2 = baseIndex2 * 4;
				vertexIndex3 = baseIndex3 * 4;

				normalIndex1 = baseIndex1 * 3;
				normalIndex2 = baseIndex2 * 3;
				normalIndex3 = baseIndex3 * 3;

				vertexBuffer.position(vertexIndex1);
				vertexBuffer.get(vertex1, 0, 3);

				vertexBuffer.position(vertexIndex2);
				vertexBuffer.get(vertex2, 0, 3);

				vertexBuffer.position(vertexIndex3);
				vertexBuffer.get(vertex3, 0, 3);

				Vertex.subtract(vertex1, vertex2, edge1);
				Vertex.subtract(vertex3, vertex2, edge2);

				Vector.crossProduct(edge1, edge2, tempNormal);
				Vector.normalize(tempNormal, normal);

				normalBuffer.position(normalIndex1);
				normalBuffer.put(normal, 0, 3);
				normalBuffer.position(normalIndex2);
				normalBuffer.put(normal, 0, 3);
				normalBuffer.position(normalIndex3);
				normalBuffer.put(normal, 0, 3);

				break;
			}
		}
		indexBuffer.rewind();
		vertexBuffer.rewind();
		normalBuffer.rewind();

		out.printf("done.\n");
		return normalBuffer;
	}

//	private FloatBuffer calculateColors(int primitiveType, TerrainData imageData,
//			IntBuffer indexBuffer) {
//
//		int width = imageData.getWidth();
//		int height = imageData.getHeight();
//		Raster raster = imageData.getAsBufferedImage().getData();
//		out.printf("Loading texture image array...");
//		int numColors = (width + 1) * (height + 1) * 4;
//		FloatBuffer colorBuffer = BufferUtils.allocateFloatBuffer(numColors);
//		float color[] = new float[4];
//
//		int colorIndex1, colorIndex2, colorIndex3, colorIndex4;
//		// TODO Fix color array generation loop
//
//		//ColorModel colorModel = imageData.getAsBufferedImage().getColorModel();
//
//		
//		int numComponents = imageData.getNumBands();//colorModel.getNumComponents();
//		float max[] = new float[numComponents];
//		for (int i = 0; i < numComponents; i++) {
//			max[i] = (float) Math.pow(2.0, (double) colorModel
//					.getComponentSize(i));
//		}
//		for (int y = 0; y < imageSize.height; y++)
//			for (int x = 0; x < imageSize.width; x++) {
//
//				color[0] = raster.getSampleFloat(x, y, 0) / max[0];
//				color[1] = raster.getSampleFloat(x, y, 1) / max[1];
//				color[2] = raster.getSampleFloat(x, y, 2) / max[2];
//				color[3] = 0.0f;
//
//				switch (primitiveType) {
//
//				case GL.GL_QUADS:
//					colorIndex1 = indexBuffer.get() * 4;
//					colorIndex2 = indexBuffer.get() * 4;
//					colorIndex3 = indexBuffer.get() * 4;
//					colorIndex4 = indexBuffer.get() * 4;
//
//					colorBuffer.position(colorIndex1);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex2);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex3);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex4);
//					colorBuffer.put(color);
//
//					break;
//				case GL.GL_TRIANGLES:
//					colorIndex1 = indexBuffer.get() * 4;
//					colorIndex2 = indexBuffer.get() * 4;
//					colorIndex3 = indexBuffer.get() * 4;
//
//					colorBuffer.position(colorIndex1);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex2);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex3);
//					colorBuffer.put(color);
//
//					colorIndex1 = indexBuffer.get() * 4;
//					colorIndex2 = indexBuffer.get() * 4;
//					colorIndex3 = indexBuffer.get() * 4;
//
//					colorBuffer.position(colorIndex1);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex2);
//					colorBuffer.put(color);
//					colorBuffer.position(colorIndex3);
//					colorBuffer.put(color);
//					break;
//				}
//
//			}
//		indexBuffer.rewind();
//		colorBuffer.rewind();
//
//		out.printf("done.\n");
//		return colorBuffer;
//	}

	public int getPrimitiveType() {
		return primitiveType;
	}

	public void setPrimitiveType(int primitiveType) {
		this.primitiveType = primitiveType;
	}

	/*
	 
	 public void setFilter(DataFilter filter) {
		this.filter = filter;
		init();
	}
	*/

}
