package projectzombie.display;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glViewport;

import java.nio.ByteBuffer;

import org.lwjgl.opengl.GL33;

import gl_engine.MathHelpers;
import gl_engine.matrix.Matrix4;
import gl_engine.vec.Vec3d;
import projectzombie.Main;
import projectzombie.entity.player.EntityPlayer;
import projectzombie.model.Model;
import projectzombie.util.math.ColorRange;
import projectzombie.world.chunk.Chunk;
import projectzombie.world.chunk.ChunkEventHandler;
import projectzombie.world.layer.layergen.LayerGen;

public class DisplayRender
{
	public static int shadow_fbo;
	public static int shadow_depth;
	
	private static final int SHADOW_SIZE = 4096;
	
	private static int generateDepthTexture(int width, int height)
	{
		int depth = GL33.glGenTextures();
		
		GL33.glBindTexture(GL33.GL_TEXTURE_2D, depth);
		
		GL33.glTexImage2D(
				GL33.GL_TEXTURE_2D, 0, GL33.GL_DEPTH_COMPONENT32, width, height,
				0, GL33.GL_DEPTH_COMPONENT, GL33.GL_FLOAT, (ByteBuffer) null);
		
		GL33.glTexParameteri(GL33.GL_TEXTURE_2D, GL33.GL_TEXTURE_MIN_FILTER, GL33.GL_LINEAR);
		GL33.glTexParameteri(GL33.GL_TEXTURE_2D, GL33.GL_TEXTURE_MAG_FILTER, GL33.GL_LINEAR);
		GL33.glFramebufferTexture(GL33.GL_FRAMEBUFFER, GL33.GL_DEPTH_ATTACHMENT, depth, 0);
		
		return depth;
	}
	
	private static int generateColorTexture(int width, int height)
	{
		int color = GL33.glGenTextures();
		
		GL33.glBindTexture(GL33.GL_TEXTURE_2D, color);
		
		GL33.glTexImage2D(
				GL33.GL_TEXTURE_2D, 0, GL33.GL_RGB, width, height,
				0, GL33.GL_RGB, GL33.GL_FLOAT, (ByteBuffer) null);
		
		GL33.glTexParameteri(GL33.GL_TEXTURE_2D, GL33.GL_TEXTURE_MIN_FILTER, GL33.GL_LINEAR);
		GL33.glTexParameteri(GL33.GL_TEXTURE_2D, GL33.GL_TEXTURE_MAG_FILTER, GL33.GL_LINEAR);
		GL33.glFramebufferTexture(GL33.GL_FRAMEBUFFER, GL33.GL_COLOR_ATTACHMENT0, color, 0);
		
		return color;
	}
	
	public static void init()
	{
		shadow_fbo = GL33.glGenFramebuffers();
		GL33.glBindFramebuffer(GL33.GL_FRAMEBUFFER, shadow_fbo);
		GL33.glDrawBuffer(GL33.GL_NONE);
		GL33.glReadBuffer(GL33.GL_NONE);
		
		shadow_depth = generateDepthTexture(SHADOW_SIZE, SHADOW_SIZE);
	}
	
	public static void render(int w, int h)
	{
		// Setup GL and clear the colour
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0, 0, w, h);
		
		GL33.glEnable(GL33.GL_DEPTH_TEST);
		GL33.glUniform4f(Main.window.glsl_color, 1, 1, 1, 1);
		GL33.glUniform2f(Main.window.glsl_tex_cut, 0, 0);
		
		ColorRange range = Main.world.getLayer().layergen.getLightLevel();
		
		GL33.glUniform3f(Main.window.glsl_day_low, (float)range.min.x, (float)range.min.y, (float)range.min.z);
		GL33.glUniform3f(Main.window.glsl_day_high, (float)range.max.x, (float)range.max.y, (float)range.max.z);
		
		if(Main.menu.doGameRender)
		{
			if(ChunkEventHandler.loaded)
			{
				EntityPlayer player = Main.player;
				Camera camera = new Camera(w, h);
				Camera.camera = camera;
				
				Matrix4 rotated = Matrix4.rotate(-camera.angle, 0, 1, 0);
				Matrix4 billboard = Matrix4.multiply(Matrix4.rotate(-45, 1, 0, 0), rotated);
				
				Main.window.environmentRenderer.use();
				GL33.glUniformMatrix4fv(Main.window.glsl_billboard, true, billboard.getArray());
				GL33.glUniformMatrix4fv(Main.window.glsl_rotated, true, rotated.getArray());
				GL33.glUniformMatrix4fv(Main.window.glsl_projection_sun, true, Camera.camera.projection_sun.getArray());
				GL33.glUniform1i(Main.window.glsl_time, (int)((System.currentTimeMillis() / 10) % 1000));
				
				Main.world.markPoolDirty();
				
				// Render from the sun/moons perspective
				GL33.glBindFramebuffer(GL33.GL_FRAMEBUFFER, shadow_fbo);
				glClear(GL_DEPTH_BUFFER_BIT);
				glViewport(0, 0, SHADOW_SIZE, SHADOW_SIZE);
				
				GL33.glUniformMatrix4fv(Main.window.glsl_projection, true, Camera.camera.projection_sun.getArray());
				LayerGen layergen = Main.world.getLayer().layergen;
				double sunPosition = MathHelpers.mod(layergen.getSunPosition(), 360);
				
				if(layergen.hasSun() && sunPosition > 0 && sunPosition < 180) {
					Main.world.render(camera);
				}
				
				// Render from the player perspective
				GL33.glBindFramebuffer(GL33.GL_FRAMEBUFFER, 0);
				glViewport(0, 0, w, h);
				
				GL33.glUniformMatrix4fv(Main.window.glsl_projection, true, Camera.camera.projection.getArray());
				Main.world.render(camera);
				
				player.chunk = Main.world.getLayer().getChunk(player.getPos().xz());
			}
		}
		
		// Render the user interface
		DisplayRenderUI.render();
	}
}
