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

import static java.lang.System.out;

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

import javax.media.opengl.GL;
import javax.media.opengl.GLContext;

import edu.uprm.walsaip.vte.core.ModuleInfo;
import edu.uprm.walsaip.vte.core.messagebus.Message;
import edu.uprm.walsaip.vte.core.messagebus.MessageBus;
import edu.uprm.walsaip.vte.core.util.BufferUtils;
import edu.uprm.walsaip.vte.core.util.StatCounter;

public class GeoMipMapRenderer implements Renderer{
	public static final String ID = GeoMipMapRenderer.class.getSimpleName();
	public static final String TYPE = Renderer.ID;
	public static final ModuleInfo<GeoMipMapRenderer> MODULE_INFO = new ModuleInfo<GeoMipMapRenderer>() {
		

		protected void setInfo() {
			name = GeoMipMapRenderer.ID;
			description = "GeoMipMap Vertex Array Renderer";
			moduleClass = GeoMipMapRenderer.class;
		
		}
		
		public boolean isSupported() {
			GL gl = GLContext.getCurrent().getGL();
			return gl.isFunctionAvailable("glDrawArrays");  
		}
	};
	
	private StatCounter counter = null;
	private int polygonType = GL.GL_FILL;
	private boolean colorMode = true;
	private int gNumTrisRendered = 0;
		//	A vertex array to contain the coordinate data for rendering triangle strips in terrain blocks.
	private FloatBuffer vertexArray;
	private int textureIds[];
	private int nextTextureIdIndex = 0;
	//	A color array containing colors assigned to each vertex in vertexArray.
//	private FloatBuffer colorArray;
	
	
	public GeoMipMapRenderer() {
		super();
		MessageBus.addListener(this,Renderer.ID);
		counter = StatCounter.getInstance();
	}


	
	public void genTexturesIds(GL gl,int numTextures) {
		textureIds = new int[numTextures];
		gl.glGenTextures(numTextures, textureIds, 0);
		
	}

	public int getNextTextureId() {
		return textureIds[nextTextureIdIndex++];
	}
	
	public void setupBuffers(int numTriangles) {
		//	Initialize the vertex array used for rendering triangle strips.
		vertexArray = setupBuffer(BufferUtils.tryAllocateDirect(numTriangles*3*4));
		
		//	Initialize the color array.
		//colorArray = setupBuffer(BufferUtils.tryAllocateDirect(numTriangles*3*4));
	}
	
	public void init(GL gl) {
		genTexturesIds(gl,1);
//		 Enables clearing of the depth buffer
		gl.glClearDepth(1.0);

		// The type of depth test to do
		gl.glDepthFunc(GL.GL_LESS);

		// Enable Hidden surface removal
		gl.glEnable(GL.GL_DEPTH_TEST);
		//gl.glDisable(GL.GL_BLEND);
		// Cull back-facing triangles
		gl.glCullFace(GL.GL_BACK);
		gl.glEnable(GL.GL_CULL_FACE);
		gl.glFrontFace(GL.GL_CCW);
		 gl.glShadeModel(GL.GL_FLAT);//gl.glShadeModel(GL.GL_SMOOTH);
		//gl.glEnable(GL.GL_POLYGON_SMOOTH);
		//gl.glEnable(GL.GL_NORMALIZE);
		// ----------- LIGHTING SETUP -----------

		// Light values and coordinates
		float whiteLight[] = { 1f, 1f, 1f, 1.0f };
		float ambientLight[] = { 0.8f, 0.8f, 0.8f, 1.0f };
		float diffuseLight[] = { 0.50f, 0.50f, 0.50f, 1.0f };
		float lightPos[] = { 0.00f, 300.00f, 0.00f, 1.0f };

		// Setup and enable light 0
//		gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, ambientLight, 0);
//		gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambientLight, 0);
//		gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuseLight, 0);
//		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPos, 0);
//		gl.glEnable(GL.GL_LIGHT0);
//		gl.glEnable(GL.GL_LIGHTING);
		// Enable color tracking
//		gl.glEnable(GL.GL_COLOR_MATERIAL);
//
//		// Set the color for the landscape
//		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, whiteLight, 0);
		out.println(GeoMipMapRenderer.ID+" Initialization: completed");
	}

	public int render(GL gl) {
		// TODO Auto-generated method stub	
		return 0;
	}

	public int render(GL gl, int mode,int numElements) {
		
	
		gl.glDrawArrays(mode, 0, numElements);
		gNumTrisRendered += numElements-2;
		return gNumTrisRendered;
	}
	
	
	public void render_frame_begin(GL gl) {
		gNumTrisRendered = 0;
		if (colorMode) {
			gl.glColor3f(1f, 1f,1f);
			gl.glEnable(GL.GL_TEXTURE_2D);
//			for (int t = 0; t< nextTextureIdIndex;t++)
//				gl.glBindTexture(GL.GL_TEXTURE_2D, textureIds[t]);
		}
		else {
			gl.glDisable(GL.GL_TEXTURE_2D);
			gl.glColor3f(1f, 0f,0f);
		}
		gl.glPolygonMode(GL.GL_FRONT, polygonType);
		// TODO prevent multiple calls to this method until render_frame_end is invoked 

		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexArray);
		
		
	}

	public void render_frame_end(GL gl) {
		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
		counter.count(gNumTrisRendered, 1);
	}

	public void processEvent(Message<?> e) {
		// TODO handle reInit event
		
			String action = e.getName();
			Object value = e.getValue();
			if (action.equals(SET_TEXTURE)) {
				colorMode = Boolean.class.cast(value);
			}
			else if (action.equals(INIT)) {
				//
			}
			else if (action.equals(SET_POLYGON_TYPE)) {
				polygonType = Integer.class.cast(value);
			}
			
	}

	
	public int addVertex(int idx, float xPos, float yPos, float zPos) {
		
		//	Set the vertex.
	
		
		vertexArray.put(idx++, xPos);
		vertexArray.put(idx++, zPos);
		vertexArray.put(idx++, yPos);
	
		
//		vertexArray.put(idx, xPos);
//		vertexArray.put(idx + 1, zPos);
//		vertexArray.put(idx + 2, yPos);
//		
		//	Set the shading.
	//	float shade = 0.5f;//0.2f + (zPos - minAlt)*colorFactor;
//		colorArray.put(idx++, shade);
//		colorArray.put(idx++, shade);
//		colorArray.put(idx++, shade);
		/*
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		*/
		return idx;
	}

	public int addVertex(int idx, FloatBuffer vertex) {
		
		//	Set the vertex.
		
		vertexArray.put(vertex);
		
		//	Set the shading.
		
		/*
		float shade = 0.5f;//0.2f + (zPos - minAlt)*colorFactor;
		colorArray.put(idx++, shade);
		colorArray.put(idx++, shade);
		colorArray.put(idx++, shade);
		//*
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		return idx;
		*/
		return idx+3;
	}

	
	public int addVertex(int idx, float [] vertex) {
		
		//	Set the vertex.
		
		//vertexArray.put(vertex);
		vertexArray.put(idx++, vertex[0]);
		vertexArray.put(idx++, vertex[1]);
		vertexArray.put(idx++, vertex[2]);
//		vertexArray.put(idx, vertex[0]);
//		vertexArray.put(idx + 1, vertex[1]);
//		vertexArray.put(idx + 2, vertex[2]);
//		
//		//	Set the shading.
//		float shade = 0.5f;//0.2f + (zPos - minAlt)*colorFactor;
//		colorArray.put(idx++, shade);
//		colorArray.put(idx++, shade);
//		colorArray.put(idx++, shade);
		/*
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		colorArray.put(idx++, 0.75f);
		*/
		return idx;
	}

	

	private static FloatBuffer setupBuffer(ByteBuffer buf) {
		buf.order(ByteOrder.nativeOrder());
		return buf.asFloatBuffer();
	}
	
	public int numTrianglesRendered() {
		return gNumTrisRendered;
	}
}
