package xaero.rotatenjump.game.graphics.model;

import android.opengl.GLES20;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import xaero.rotatenjump.game.graphics.Graphics;
import xaero.rotatenjump.game.graphics.Texture;

/**
 * Created by xaero on 18/08/2017.
 */

public class Model {

    static final int POSITION_SIZE = 3;
    static final int UV_SIZE = 2;
    static final int NORMAL_SIZE = 3;
    static final int COLOUR_SIZE = 4;
    static final int VERTEX_SIZE = POSITION_SIZE + UV_SIZE + NORMAL_SIZE + COLOUR_SIZE /* + others*/;
    static final int STRIDE = VERTEX_SIZE * 4;

    private boolean uploaded = false;
    private int VBO;
    private int IBO;
    private FloatBuffer vertexBuffer;//all data packed together
    private ShortBuffer indexBuffer;
    private static float[] modelMatrix = new float[16];
    private static float[] normalMatrix = new float[16];//anton version
    private int numberOfIndexes;
    private Texture texture;

    public void create(float[] vertexArray, short[] indexArray){
        create(vertexArray, indexArray, null);
    }

    public void create(float[] vertexArray, short[] indexArray, Texture texture){
        this.texture = texture;

        ByteBuffer bb = ByteBuffer.allocateDirect(vertexArray.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(vertexArray);
        vertexBuffer.position(0);

        bb = ByteBuffer.allocateDirect(indexArray.length * 2);
        bb.order(ByteOrder.nativeOrder());
        indexBuffer = bb.asShortBuffer();
        indexBuffer.put(indexArray);
        indexBuffer.position(0);
        numberOfIndexes = indexArray.length;
    }

    public void draw(float x, float y, float rotationZ, float rotationY, float rotationX, float scaleX, float scaleY, float scaleZ){
        draw(x,y, 0, rotationZ, rotationY, rotationX, scaleX, scaleY, scaleZ);
    }

    public void draw(float x, float y, float z, float rotationZ, float rotationY, float rotationX, float scaleX, float scaleY, float scaleZ){
        draw( x, y, z, rotationZ, rotationY, rotationX, scaleX, scaleY, scaleZ, null);
    }

    public void draw(float x, float y, float rotationZ, float rotationY, float rotationX, float scaleX, float scaleY, float scaleZ, FloatBuffer customUV){
        draw(x, y, 0, rotationZ, rotationY, rotationX, scaleX, scaleY, scaleZ, customUV);
    }

    public void draw(float x, float y, float z, float rotationZ, float rotationY, float rotationX, float scaleX, float scaleY, float scaleZ, FloatBuffer customUV){
        if(!uploaded){
            upload();
            uploaded = true;
        }
        Matrix.setIdentityM(modelMatrix, 0);
        if(x != 0 || y != 0 || z != 0)
            Matrix.translateM(modelMatrix, 0, modelMatrix, 0, x, y, z);
        if(rotationZ != 0 || rotationY != 0 || rotationX != 0) {
            Matrix.rotateM(modelMatrix, 0, rotationZ, 0, 0, 1);
            Matrix.rotateM(modelMatrix, 0, rotationX, 1, 0, 0);
            Matrix.rotateM(modelMatrix, 0, rotationY, 0, 1, 0);
        } if(scaleX != 1 || scaleY != 1 || scaleZ != 1)
            Matrix.scaleM(modelMatrix, 0, modelMatrix, 0, scaleX, scaleY, scaleZ);
        if(Graphics.lighting){
            Matrix.setIdentityM(normalMatrix, 0);
            if(rotationZ != 0 || rotationY != 0 || rotationX != 0) {
                Matrix.rotateM(normalMatrix, 0, rotationZ, 0, 0, 1);
                Matrix.rotateM(normalMatrix, 0, rotationX, 1, 0, 0);
                Matrix.rotateM(normalMatrix, 0, rotationY, 0, 1, 0);
            }
        }
        Graphics.sendMatrices(modelMatrix, normalMatrix);
        GLES20.glUniform4fv(Graphics.getColourLocation(), 1, Graphics.color, 0);

        if(texture != null) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            texture.bindTexture();
            GLES20.glUniform1i(Graphics.getTextureLocation(), 0);//0 is GLES20.GL_TEXTURE0
            GLES20.glUniform1i(Graphics.getUseTextureLocation(), 1);
        } else {
            Texture.unbindTexture();
            GLES20.glUniform1i(Graphics.getUseTextureLocation(), 0);
        }

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO);
        GLES20.glEnableVertexAttribArray(Graphics.getPositionLocation());
        GLES20.glEnableVertexAttribArray(Graphics.getUvLocation());
        GLES20.glEnableVertexAttribArray(Graphics.getNormalLocation());
        GLES20.glEnableVertexAttribArray(Graphics.getVertexColourLocation());
        GLES20.glVertexAttribPointer(Graphics.getPositionLocation(), POSITION_SIZE, GLES20.GL_FLOAT, false, STRIDE, 0);
        GLES20.glVertexAttribPointer(Graphics.getNormalLocation(), NORMAL_SIZE, GLES20.GL_FLOAT, false, STRIDE, (POSITION_SIZE + UV_SIZE) * 4);//after UV actually
        GLES20.glVertexAttribPointer(Graphics.getVertexColourLocation(), COLOUR_SIZE, GLES20.GL_FLOAT, false, STRIDE, (POSITION_SIZE + UV_SIZE + NORMAL_SIZE) * 4);
        if(customUV == null) {
            GLES20.glVertexAttribPointer(Graphics.getUvLocation(), UV_SIZE, GLES20.GL_FLOAT, false, STRIDE, POSITION_SIZE * 4);
        } else {
            GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
            GLES20.glVertexAttribPointer(Graphics.getUvLocation(), UV_SIZE, GLES20.GL_FLOAT, false, 2 * 4, customUV);
        }
        //GLES20.glEnableVertexAttribArray(something like, like normal data);
        //STRIDE should be changed to include the normal data
        //GLES20.glVertexAttribPointer(Graphics.getPositionLocation(), NORMAL_SIZE, GLES20.GL_FLOAT, false, STRIDE, offset in bytes so POSITION_SIZE * 4);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, IBO);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, numberOfIndexes, GLES20.GL_UNSIGNED_SHORT, 0);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
        GLES20.glDisableVertexAttribArray(Graphics.getPositionLocation());
        GLES20.glDisableVertexAttribArray(Graphics.getUvLocation());
        GLES20.glDisableVertexAttribArray(Graphics.getNormalLocation());
        GLES20.glDisableVertexAttribArray(Graphics.getVertexColourLocation());
    }

    private void upload(){
        int[] buffers = new int[2];
        GLES20.glGenBuffers(2, buffers, 0);
        VBO = buffers[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexBuffer.capacity() * 4 /*float size*/, vertexBuffer, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
        vertexBuffer.clear();
        vertexBuffer = null;

        IBO = buffers[1];
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, IBO);
        GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.capacity() * 2 /*short size*/, indexBuffer, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
        indexBuffer.clear();
        indexBuffer = null;
    }

}
