package com.github.hydos.ginger.engine.render.renderers;

import java.util.*;

import org.joml.Vector4f;
import org.lwjgl.opengl.*;

import com.github.hydos.ginger.engine.cameras.Camera;
import com.github.hydos.ginger.engine.elements.objects.*;
import com.github.hydos.ginger.engine.io.Window;
import com.github.hydos.ginger.engine.math.Maths;
import com.github.hydos.ginger.engine.math.matrixes.Matrix4f;
import com.github.hydos.ginger.engine.render.*;
import com.github.hydos.ginger.engine.render.models.*;
import com.github.hydos.ginger.engine.render.shaders.NormalMappingShader;
import com.github.hydos.ginger.engine.render.texture.ModelTexture;

public class NormalMappingRenderer extends Renderer
{
	private NormalMappingShader shader;

	public NormalMappingRenderer(Matrix4f projectionMatrix)
	{
		this.shader = new NormalMappingShader();
		shader.start();
		shader.loadProjectionMatrix(projectionMatrix);
		shader.connectTextureUnits();
		shader.stop();
	}

	public void cleanUp()
	{ shader.cleanUp(); }

	private void prepare(Vector4f clipPlane, List<Light> lights, Camera camera)
	{
		shader.loadClipPlane(clipPlane);
		//need to be public variables in MasterRenderer
		shader.loadSkyColour(Window.getColour());
		Matrix4f viewMatrix = Maths.createViewMatrix(camera);
		shader.loadLights(lights, viewMatrix);
		shader.loadViewMatrix(viewMatrix);
	}

	private void prepareInstance(RenderObject entity)
	{
		Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotX(),
			entity.getRotY(), entity.getRotZ(), entity.getScale());
		shader.loadTransformationMatrix(transformationMatrix);
		shader.loadOffset(0, 0);
	}

	private void prepareTexturedModel(TexturedModel model)
	{
		RawModel rawModel = model.getRawModel();
		GL30.glBindVertexArray(rawModel.getVaoID());
		GL20.glEnableVertexAttribArray(0);
		GL20.glEnableVertexAttribArray(1);
		GL20.glEnableVertexAttribArray(2);
		GL20.glEnableVertexAttribArray(3);
		ModelTexture texture = model.getTexture();
		if (texture.isTransparent())
		{ MasterRenderer.disableCulling(); }
		shader.loadShineVariables(texture.getShineDamper(), texture.getReflectivity());
		GL13.glActiveTexture(GL13.GL_TEXTURE0);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getTexture().getTextureID());
		GL13.glActiveTexture(GL13.GL_TEXTURE1);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getTexture().getNormalMap());
	}

	public void render(Map<TexturedModel, List<RenderObject>> entities, Vector4f clipPlane, List<Light> lights, Camera camera)
	{
		shader.start();
		prepare(clipPlane, lights, camera);
		for (TexturedModel model : entities.keySet())
		{
			prepareTexturedModel(model);
			List<RenderObject> batch = entities.get(model);
			for (RenderObject entity : batch)
			{
				prepareInstance(entity);
				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
			}
			unbindTexturedModel();
		}
		shader.stop();
		entities.clear();
	}

	private void unbindTexturedModel()
	{
		MasterRenderer.enableCulling();
		GL20.glDisableVertexAttribArray(0);
		GL20.glDisableVertexAttribArray(1);
		GL20.glDisableVertexAttribArray(2);
		GL20.glDisableVertexAttribArray(3);
		GL30.glBindVertexArray(0);
	}
}
