package engine.render;

import engine.object3d.Mesh;
import engine.object3d.Object3d;
import engine.object3d.camera.Camera;
import engine.shader.ShaderProgram;
import org.lwjgl.BufferUtils;

import java.nio.FloatBuffer;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL20.glUseProgram;

public class Renderer {

    //TODO: add some sort of caching so that we can change ShaderPrograms and send buffer data less often

    private FloatBuffer projectionBuffer;
    private FloatBuffer viewBuffer;
    private FloatBuffer modelBuffer;

    public Renderer() {
        projectionBuffer = BufferUtils.createFloatBuffer(16);
        viewBuffer = BufferUtils.createFloatBuffer(16);
        modelBuffer = BufferUtils.createFloatBuffer(16);
    }

    public void render(Scene scene, Camera camera) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        camera.getProjection().get(projectionBuffer);
        camera.getView().get(viewBuffer);

        for (Object3d obj: scene.getObjects()) {
            drawObject(obj);
        }

        glUseProgram(0);
    }

    private void drawObject(Object3d obj) {
        if (obj instanceof Mesh) {
            Mesh mesh = (Mesh) obj;

            mesh.getModel().get(modelBuffer);

            ShaderProgram program = mesh.getMaterial().getProgram();
            glUseProgram(program.getProgramId());

            //send buffer data to opengl shader uniforms (the ones for this ShaderProgram)
            glUniformMatrix4fv(program.getUniformLocation("projection"), false, projectionBuffer);
            glUniformMatrix4fv(program.getUniformLocation("view"), false, viewBuffer);
            glUniformMatrix4fv(program.getUniformLocation("model"), false, modelBuffer);

            mesh.bindVao();
            mesh.enableVertexAttributes();

            if (mesh.isIndexed()) {
                //todo: move these into the mesh if possible to clean this up a bit.
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.getIndicesVboID());
                glDrawElements(mesh.getGeometry().getDrawMode(), mesh.getIndicesCount(), GL_UNSIGNED_SHORT, 0);
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
            } else {
                glDrawArrays(mesh.getGeometry().getDrawMode(), 0, mesh.getVerticesCount());
            }

            mesh.disableVertexAttributes();
            mesh.unbindVao();
        }

        for (Object3d child: obj.getChildren()) {
            drawObject(child);
        }
    }

    public void destroy() {
        projectionBuffer = null;
        viewBuffer = null;
        modelBuffer = null;
    }

}

@@ -500,115 +500,8 @@
  %7B%0A%0A
-    //TODO: add some sort of caching so that we can change ShaderPrograms and send buffer data less often%0A%0A
     
@@ -612,16 +612,59 @@
 uffer;%0A%0A
+    private ShaderProgram currentProgram;%0A%0A
     publ
@@ -850,24 +850,55 @@
 Buffer(16);%0A
+        currentProgram = null;%0A
     %7D%0A%0A    p
@@ -1200,29 +1200,35 @@
         
-glUse
+clearShader
 Program(
 0);%0A    
@@ -1219,17 +1219,16 @@
 Program(
-0
 );%0A    %7D
@@ -1361,55 +1361,21 @@
     
-mesh.getModel().get(modelBuffer);%0A%0A            
+updateCurrent
 Shad
@@ -1387,19 +1387,9 @@
 gram
- program = 
+(
 mesh
@@ -1411,26 +1411,28 @@
 getProgram()
+)
 ;%0A
+%0A
             
@@ -1435,315 +1435,90 @@
     
-glUseProgram(program.getProgramId());%0A%0A            //send buffer data to opengl shader uniforms (the ones for this ShaderProgram)%0A            glUniformMatrix4fv(program.getUniformLocation(%22projection%22), false, projectionBuffer);%0A            glUniformMatrix4fv(program.getUniformLocation(%22view%22), false, view
+//get and send buffer data for current model%0A            mesh.getModel().get(model
 Buff
@@ -1553,17 +1553,24 @@
 trix4fv(
-p
+currentP
 rogram.g
@@ -2364,32 +2364,678 @@
        %7D%0A    %7D%0A%0A
+    private void updateCurrentShaderProgram(ShaderProgram program) %7B%0A        if (currentProgram == null %7C%7C currentProgram != program) %7B%0A            currentProgram = program;%0A            glUseProgram(currentProgram.getProgramId());%0A%0A            //re-send buffer data to opengl shader uniforms (the ones for this ShaderProgram)%0A            glUniformMatrix4fv(currentProgram.getUniformLocation(%22projection%22), false, projectionBuffer);%0A            glUniformMatrix4fv(currentProgram.getUniformLocation(%22view%22), false, viewBuffer);%0A        %7D%0A    %7D%0A%0A    private void clearShaderProgram() %7B%0A        glUseProgram(0);%0A        currentProgram = null;%0A    %7D%0A%0A
     public void 
@@ -3130,21 +3130,52 @@
 fer = null;%0A
+        currentProgram = null;%0A
     %7D%0A%0A%7D%0A

add shaderProgram caching to renderer