package engine.object3d;

import engine.geometry.Geometry;
import engine.material.Material;
import engine.opengl.vao.VertexArrayObject;
import engine.opengl.vbo.*;
import engine.shader.ShaderProgram;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;

import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;

public class Mesh extends Object3d {

    //TODO: check destroy.

    private Geometry geometry;
    private Material material;
    private VertexArrayObject vao;

    public Mesh(Geometry g, Material m) {
        geometry = g;
        material = m;
        setupBuffers();
    }

    public void disableVertexAttributes() {
        material.getProgram().getAttributeLocations().forEach(location -> glDisableVertexAttribArray(location));
    }

    public Geometry getGeometry() {
        return geometry;
    }

    public Material getMaterial() {
        return material;
    }

    public Matrix4f getModel() {
        return getLocalToWorld();
    }

    private void setupBuffers() {
        ShaderProgram program = material.getProgram();

        vao = new VertexArrayObject(geometry.isIndexed());

        VertexBufferObject verticesVbo = new FloatVertexBufferObject(setupFloatBuffer(geometry.getVertices()));
        verticesVbo.addVertexAttributePointer(new VertexAttributePointer(
                program.getAttributeLocation(program.positionAttributeName),
                4,
                VertexAttributePointer.DATA_TYPE_FLOAT
        ));
        vao.setVerticesVbo(verticesVbo);

        VertexBufferObject colorsVbo = new FloatVertexBufferObject(setupFloatBuffer(geometry.getColors()));
        colorsVbo.addVertexAttributePointer(new VertexAttributePointer(
                program.getAttributeLocation(program.colorAttributeName),
                4,
                VertexAttributePointer.DATA_TYPE_FLOAT
        ));
        vao.setColorsVbo(colorsVbo);

        if (geometry.isIndexed()) {
            IndexBufferObject indicesVbo = new ShortIndexBufferObject(setupShortBuffer(geometry.getIndices()));
            vao.setIndicesVbo(indicesVbo);
        }

        vao.sendVboDataAutoBind();
    }

    public void updateColors() {
        float[] colors = geometry.getColors();
        FloatBuffer tempBuffer = BufferUtils.createFloatBuffer(colors.length);
        tempBuffer.put(colors);
        tempBuffer.flip();

        vao.getColorsVbo().setBufferData(tempBuffer);

        vao.bind();
        {
            vao.getColorsVbo().sendDataAutoBind();
        }
        vao.unbind();
    }

    public void updateVertices() {
        float[] vertices = geometry.getVertices();
        FloatBuffer tempBuffer = BufferUtils.createFloatBuffer(vertices.length);
        tempBuffer.put(vertices);
        tempBuffer.flip();

        vao.getVerticesVbo().setBufferData(tempBuffer);

        vao.bind();
        {
            vao.getVerticesVbo().sendDataAutoBind();
        }
        vao.unbind();
    }

    public VertexArrayObject getVao() {
        return vao;
    }

    private FloatBuffer setupFloatBuffer(float[] array) {
        FloatBuffer buffer = BufferUtils.createFloatBuffer(array.length);
        buffer.put(array); buffer.flip();
        return buffer;
    }

    private ShortBuffer setupShortBuffer(short[] array) {
        ShortBuffer buffer = BufferUtils.createShortBuffer(array.length);
        buffer.put(array); buffer.flip();
        return buffer;
    }

    public void destroy() {
        deleteVao();
        material.destroyShaderProgram();
    }

    private void deleteVao() {
        vao.bind();
        {
            disableVertexAttributes();
            deleteBuffers();
        }
        vao.unbind();
        vao.delete();
    }

    private void deleteBuffers() {
        vao.getVerticesVbo().delete();
        vao.getColorsVbo().delete();
        if (geometry.isIndexed()) {
            vao.getIndicesVbo().delete();
        }
    }

}

@@ -2237,33 +2237,42 @@
     
-float%5B%5D colors = geometry
+VertexBufferObject colorsVbo = vao
 .get
@@ -2277,16 +2277,19 @@
 etColors
+Vbo
 ();%0A    
@@ -2300,37 +2300,33 @@
 FloatBuffer 
-tempB
+b
 uffer = 
 BufferUtils.
@@ -2317,122 +2317,56 @@
 r = 
-BufferUtils.createFloatBuffer(colors.length);%0A        tempBuffer.put(colors);%0A        tempBuffer.flip(
+setupFloatBuffer(geometry.getColors()
 );%0A
-%0A
         
 vao.
@@ -2361,32 +2361,25 @@
         
-vao.getC
+c
 olorsVbo
 ().setBu
@@ -2366,26 +2366,24 @@
    colorsVbo
-()
 .setBufferDa
@@ -2377,37 +2377,33 @@
 o.setBufferData(
-tempB
+b
 uffer);%0A%0A       
@@ -2437,32 +2437,25 @@
         
-vao.getC
+c
 olorsVbo
 ().sendD
@@ -2442,26 +2442,24 @@
    colorsVbo
-()
 .sendDataAut
@@ -2553,35 +2553,44 @@
     
-float%5B%5D vertices = geometry
+VertexBufferObject verticesVbo = vao
 .get
@@ -2597,16 +2597,19 @@
 Vertices
+Vbo
 ();%0A    
@@ -2628,21 +2628,17 @@
 fer 
-tempB
+b
 uffer = 
 Buff
@@ -2637,117 +2637,49 @@
 r = 
-BufferUtils.createFloatBuffer(vertices.length);%0A        tempBuffer.put(vertices);%0A        tempBuffer.flip();%0A
+setupFloatBuffer(geometry.getVertices());
 %0A   
@@ -2676,39 +2676,32 @@
 es());%0A        v
-ao.getV
 erticesVbo().set
@@ -2694,18 +2694,16 @@
 ticesVbo
-()
 .setBuff
@@ -2713,13 +2713,9 @@
 ata(
-tempB
+b
 uffe
@@ -2754,39 +2754,32 @@
  %7B%0A            v
-ao.getV
 erticesVbo().sen
@@ -2772,18 +2772,16 @@
 ticesVbo
-()
 .sendDat

Clean up some more code in Mesh