package electrosphere.renderer.meshgen;

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

import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;

import electrosphere.renderer.model.Mesh;

public class GeometryMeshGen {
    
    /**
     * Meshes a box
     * @param verts The list of verts to store into
     * @param normals The list of normals to store into
     * @param uvs The list of uvs to store into
     * @param indices The list of indices to store into
     * @param width The width of the box
     * @param height The height of the box
     * @param depth THe depth of the box
     */
    public static Mesh genBox(float width, float height, float depth){
        Mesh mesh = new Mesh("box");
        mesh.generateVAO();
        List<Vector3f> verts = new LinkedList<Vector3f>();
        List<Vector3f> normals = new LinkedList<Vector3f>();
        List<Vector2f> uvs = new LinkedList<Vector2f>();
        List<Integer> indices = new LinkedList<Integer>();
        //
        //face 1
        //

        //verts
        verts.add(new Vector3f(0,            0,            0));
        verts.add(new Vector3f(0 + width,    0,            0));
        verts.add(new Vector3f(0,            0 + height,   0));
        verts.add(new Vector3f(0 + width,    0 + height,   0));
        //indices
        indices.add(0);
        indices.add(2);
        indices.add(3);
        indices.add(0);
        indices.add(3);
        indices.add(1);
        //normals
        normals.add(new Vector3f(0,0,-1));
        normals.add(new Vector3f(0,0,-1));
        normals.add(new Vector3f(0,0,-1));
        normals.add(new Vector3f(0,0,-1));
        //uvs
        uvs.add(new Vector2f(     0,      0));
        uvs.add(new Vector2f(width,      0));
        uvs.add(new Vector2f(     0, height));
        uvs.add(new Vector2f(width, height));

        //
        //face 2
        //

        //verts
        verts.add(new Vector3f(0,  0,            0        ));
        verts.add(new Vector3f(0,  0,            0 + depth));
        verts.add(new Vector3f(0,  0 + height,   0        ));
        verts.add(new Vector3f(0,  0 + height,   0 + depth));
        //indices
        indices.add(4);
        indices.add(7);
        indices.add(6);
        indices.add(4);
        indices.add(5);
        indices.add(7);
        //normals
        normals.add(new Vector3f(-1,0,0));
        normals.add(new Vector3f(-1,0,0));
        normals.add(new Vector3f(-1,0,0));
        normals.add(new Vector3f(-1,0,0));
        //uvs
        uvs.add(new Vector2f(    0,      0));
        uvs.add(new Vector2f(depth,      0));
        uvs.add(new Vector2f(    0, height));
        uvs.add(new Vector2f(depth, height));

        //
        //face 3
        //

        //verts
        verts.add(new Vector3f(0,           0,   0        ));
        verts.add(new Vector3f(0,           0,   0 + depth));
        verts.add(new Vector3f(0 + width,   0,   0        ));
        verts.add(new Vector3f(0 + width,   0,   0 + depth));
        //indices
        indices.add( 8);
        indices.add(10);
        indices.add(11);
        indices.add( 9);
        indices.add( 8);
        indices.add(11);
        //normals
        normals.add(new Vector3f(0,-1,0));
        normals.add(new Vector3f(0,-1,0));
        normals.add(new Vector3f(0,-1,0));
        normals.add(new Vector3f(0,-1,0));
        //uvs
        uvs.add(new Vector2f(    0,      0));
        uvs.add(new Vector2f(depth,      0));
        uvs.add(new Vector2f(    0, width));
        uvs.add(new Vector2f(depth, width));

        //
        //face 4
        //

        //verts
        verts.add(new Vector3f(0,            0,            0 + depth));
        verts.add(new Vector3f(0 + width,    0,            0 + depth));
        verts.add(new Vector3f(0,            0 + height,   0 + depth));
        verts.add(new Vector3f(0 + width,    0 + height,   0 + depth));
        //indices
        indices.add(12);
        indices.add(15);
        indices.add(14);
        indices.add(12);
        indices.add(13);
        indices.add(15);
        //normals
        normals.add(new Vector3f(0,0,1));
        normals.add(new Vector3f(0,0,1));
        normals.add(new Vector3f(0,0,1));
        normals.add(new Vector3f(0,0,1));
        //uvs
        uvs.add(new Vector2f(     0,      0));
        uvs.add(new Vector2f(width,       0));
        uvs.add(new Vector2f(     0, height));
        uvs.add(new Vector2f(width,  height));


        //
        //face 5
        //

        //verts
        verts.add(new Vector3f(0 + width,  0,            0        ));
        verts.add(new Vector3f(0 + width,  0,            0 + depth));
        verts.add(new Vector3f(0 + width,  0 + height,   0        ));
        verts.add(new Vector3f(0 + width,  0 + height,   0 + depth));
        //indices
        indices.add(16);
        indices.add(18);
        indices.add(19);
        indices.add(16);
        indices.add(19);
        indices.add(17);
        //normals
        normals.add(new Vector3f(1,0,0));
        normals.add(new Vector3f(1,0,0));
        normals.add(new Vector3f(1,0,0));
        normals.add(new Vector3f(1,0,0));
        //uvs
        uvs.add(new Vector2f(    0,      0));
        uvs.add(new Vector2f(depth,      0));
        uvs.add(new Vector2f(    0, height));
        uvs.add(new Vector2f(depth, height));


        //
        //face 6
        //

        //verts
        verts.add(new Vector3f(0,           0 + height,   0        ));
        verts.add(new Vector3f(0,           0 + height,   0 + depth));
        verts.add(new Vector3f(0 + width,   0 + height,   0        ));
        verts.add(new Vector3f(0 + width,   0 + height,   0 + depth));
        //indices
        indices.add(20);
        indices.add(23);
        indices.add(22);
        indices.add(20);
        indices.add(21);
        indices.add(23);
        //normals
        normals.add(new Vector3f(0,1,0));
        normals.add(new Vector3f(0,1,0));
        normals.add(new Vector3f(0,1,0));
        normals.add(new Vector3f(0,1,0));
        //uvs
        uvs.add(new Vector2f(    0,      0));
        uvs.add(new Vector2f(depth,      0));
        uvs.add(new Vector2f(    0, width));
        uvs.add(new Vector2f(depth, width));

        

        //store into arrays
        FloatBuffer vertBuffer;
        FloatBuffer normalBuffer;
        FloatBuffer uvBuffer;
        IntBuffer faceBuffer;
        //verts
        float[] vertices = new float[verts.size() * 3];
        for(int i = 0; i < verts.size(); i++){
            Vector3f currentVert = verts.get(i);
            vertices[3 * i + 0] = currentVert.x;
            vertices[3 * i + 1] = currentVert.y;
            vertices[3 * i + 2] = currentVert.z;
        }
        vertBuffer = BufferUtils.createFloatBuffer(vertices.length);
        vertBuffer.put(vertices);

        //faces
        int[] faceElements = new int[indices.size()];
        for(int i = 0; i < indices.size(); i++){
            faceElements[i] = indices.get(i);
        }
        faceBuffer = BufferUtils.createIntBuffer(faceElements.length);
        faceBuffer.put(faceElements);

        //normals
        float[] normalsArr = new float[normals.size() * 3];
        for(int i = 0; i < normals.size(); i++){
            Vector3f currentNormal = normals.get(i);
            normalsArr[3 * i + 0] = currentNormal.x;
            normalsArr[3 * i + 1] = currentNormal.y;
            normalsArr[3 * i + 2] = currentNormal.z;
        }
        normalBuffer = BufferUtils.createFloatBuffer(normalsArr.length);
        normalBuffer.put(normalsArr);

        //uvs
        float[] uvsArr = new float[uvs.size() * 2];
        for(int i = 0; i < uvs.size(); i++){
            Vector2f currentUV = uvs.get(i);
            uvsArr[2 * i + 0] = currentUV.x;
            uvsArr[2 * i + 1] = currentUV.y;
        }
        uvBuffer = BufferUtils.createFloatBuffer(uvsArr.length);
        uvBuffer.put(uvsArr);




        //actually store in mesh
        int elementCount = faceElements.length;
        try {
            //actually buffer vertices
            if(vertBuffer.position() > 0){
                vertBuffer.flip();
                mesh.bufferVertices(vertBuffer, 3);
            }
            //actually buffer normals
            if(normalBuffer != null && normalBuffer.position() > 0){
                normalBuffer.flip();
                mesh.bufferNormals(normalBuffer, 3);
            }
            //actually buffer UVs
            if(uvBuffer != null && uvBuffer.position() > 0){
                uvBuffer.flip();
                mesh.bufferTextureCoords(uvBuffer, 2);
            }
            //buffer element indices
            if(faceBuffer.position() > 0){
                faceBuffer.flip();
                mesh.bufferFaces(faceBuffer, elementCount);
            }
        } catch (NullPointerException ex){
            ex.printStackTrace();
        }

        return mesh;
    }

}
