package core.engine;

import core.engine.model.HexagonModel;
import core.engine.model.RawModel;
import core.engine.textures.Texture;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

public class Loader {

    private List<Integer> vaos = new ArrayList<>();
    private List<Integer> vbos = new ArrayList<>();
    private List<Integer> textureIDs = new ArrayList<>();

    public RawModel loadToVAO(float[] positions, float[] textureCoords, float[] normals,  int[] indices) {
        int vaoID = createVAO();
        bindIndicesBuffer(indices);
        storeDataInAttributeList(0, 3,positions);
        storeDataInAttributeList(1, 2, textureCoords);
        storeDataInAttributeList(2, 3, normals);
        unbindVAO();
        return new RawModel(vaoID, indices.length);
    }

    public RawModel loadHexagon(HexagonModel hexagonModel) {
        return loadToVAO(hexagonModel.getVertices(), hexagonModel.getTextureCoords(), hexagonModel.getNormals(), hexagonModel.getIndices());
    }

    public int loadTexture(String fileName) {
        Texture texture = new Texture("res/" + fileName + ".png");
        int textureID = texture.getTextureID();
        this.textureIDs.add(textureID);
        return textureID;
    }

    private int createVAO() {
        int vao = GL30.glGenVertexArrays();
        vaos.add(vao);
        GL30.glBindVertexArray(vao);
        return vao;
    }

    private void storeDataInAttributeList(int attributeNumber, int coordinateSize, float[] data) {
        int vboID = GL15.glGenBuffers();
        vbos.add(vboID);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
        FloatBuffer floatBuffer = storeInFloatBuffer(data);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, floatBuffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(attributeNumber, coordinateSize, GL11.GL_FLOAT, false, 0,0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    }

    private void unbindVAO() {
        GL30.glBindVertexArray(0);
    }

    private void bindIndicesBuffer(int[] indices) {
        int vboID = GL15.glGenBuffers();
        vbos.add(vboID);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
        IntBuffer buffer = storeInIntBuffer(indices);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
    }

    private IntBuffer storeInIntBuffer(int[] data) {
        IntBuffer intBuffer = BufferUtils.createIntBuffer(data.length);
        intBuffer.put(data);
        //Buffer is until know expected to be written to. Flip it, so it can be read
        intBuffer.flip();
        return intBuffer;
    }

    private FloatBuffer storeInFloatBuffer(float[] data) {
        FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(data.length);
        floatBuffer.put(data);
        //Buffer is until know expected to be written to. Flip it, so it can be read
        floatBuffer.flip();
        return floatBuffer;
    }


    public void cleanUp() {
        for(int vao: vaos) {
            GL30.glDeleteVertexArrays(vao);
        }

        for(int vbo: vbos) {
            GL15.glDeleteBuffers(vbo);
        }

        for(int textureID: textureIDs) {
            GL11.glDeleteTextures(textureID);
        }
    }
}
