package com.regar007.shapesinopengles20.Shapes;

import android.content.Context;
import android.opengl.GLES20;

import com.regar007.shapesinopengles20.R;
import com.regar007.shapesinopengles20.Utils.RawResourceReader;
import com.regar007.shapesinopengles20.Utils.ShaderHelper;

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

/**
 * Created by regar007.
 * This implementation uses VBOs(vertex buffer objects) to draw lines.
 * i.e., Instantiate once and draw always using just render() function.
 *
 * This class takes "Activity", " a Combination of two Points in {x1, y1, z1, x2, y2, z2} order" and "Colors in {r, g, b, a, r, g, b, a} order".
 * Use(Once): aLines = new Lines(activity, new float{-1, -1, -1, 1, 1, 1}, new float{1, 0, 0, 1, 0, 1, 0, 1});
 * Note: Use(OnDrawFrame) call createBuffer() function with changed values.
 * render function takes "MVP Matrix to draw point/points".
 * Use(OnDrawFrame): aPoint.render(mvpMatrix);
 */
public class Lines {
    private final String Tag = "Lines";
    static final int[] glLineBuffer = new int[2];

    private final int aLineProgramHandle;
    private int BYTES_PER_FLOAT = 4;
    private int POSITION_DATA_SIZE = 3;

    private int aLinePositionsBufferIdx;
    private int aLineColorsBufferIdx;
    private int vertexCount;

    /**
     * instantiate the Lines shape object
     * @param activity
     * @param positions
     * @param colors
     */
    public Lines(Context activity, float[] positions, float[] colors) {

        /** initialize the line program */
        final String lineVS = RawResourceReader.readTextFileFromRawResource(activity, R.raw.line_vertex_shader);
        final String lineFS = RawResourceReader.readTextFileFromRawResource(activity, R.raw.line_fragment_shader);

        final int lineVSHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, lineVS);
        final int lineFSHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, lineFS);

        aLineProgramHandle = ShaderHelper.createAndLinkProgram(lineVSHandle, lineFSHandle,
                new String[]{"a_Position", "a_Color"});

        // Second, copy these buffers into OpenGL's memory. After, we don't need to keep the client-side buffers around.
        GLES20.glGenBuffers(glLineBuffer.length, glLineBuffer, 0);

        createBuffers(positions, colors);
    }

    /**
     * create buffers for the Lines shape object
     * @param linePositions
     * @param lineColors
     */
    public void createBuffers(float[] linePositions, float[] lineColors) {
        final int lineDataLength = linePositions.length;
        vertexCount = linePositions.length / POSITION_DATA_SIZE;

        final FloatBuffer lineBuffer = ByteBuffer.allocateDirect(lineDataLength * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        lineBuffer.put(linePositions);
        final FloatBuffer colorBuffer = ByteBuffer.allocateDirect(lineColors.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        colorBuffer.put(lineColors);

        lineBuffer.position(0);
        colorBuffer.position(0);

        FloatBuffer linePositionsBuffer = lineBuffer;
        FloatBuffer lineColorsBuffers = colorBuffer;

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, glLineBuffer[0]);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, linePositionsBuffer.capacity() * BYTES_PER_FLOAT, linePositionsBuffer, GLES20.GL_STATIC_DRAW);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, glLineBuffer[1]);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, lineColorsBuffers.capacity() * BYTES_PER_FLOAT, lineColorsBuffers, GLES20.GL_STATIC_DRAW);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        aLinePositionsBufferIdx = glLineBuffer[0];
        aLineColorsBufferIdx = glLineBuffer[1];

        linePositionsBuffer.limit(0);
        linePositionsBuffer = null;
        lineColorsBuffers.limit(0);
        lineColorsBuffers = null;
    }

    /**
     * draws the Lines shape object
     * @param aMVPMatrix
     */
    public void render(float[] aMVPMatrix) {

        // Set our per-vertex lighting program.
        GLES20.glUseProgram(aLineProgramHandle);

        int aLineMVPMatrixHandle = GLES20.glGetUniformLocation(aLineProgramHandle, "u_MVPMatrix");
        int aLinePositionHandle = GLES20.glGetAttribLocation(aLineProgramHandle, "a_Position");
        int aLineColorHandle = GLES20.glGetAttribLocation(aLineProgramHandle, "a_Color");

        // Pass in the combined matrix.
        GLES20.glUniformMatrix4fv(aLineMVPMatrixHandle, 1, false, aMVPMatrix, 0);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, aLinePositionsBufferIdx);
        GLES20.glEnableVertexAttribArray(aLinePositionHandle);
        GLES20.glVertexAttribPointer(aLinePositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, 0);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, aLineColorsBufferIdx);
        GLES20.glEnableVertexAttribArray(aLineColorHandle);
        GLES20.glVertexAttribPointer(aLineColorHandle, 4, GLES20.GL_FLOAT, false, 0, 0);

        // Clear the currently bound buffer (so future OpenGL calls do not use this buffer).
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        // Draw the line.
        GLES20.glDrawArrays(GLES20.GL_LINES, 0, vertexCount);

    }

    /**
     * Delete buffers from OpenGL's memory
     */
    public void release() {
        // Delete buffers from OpenGL's memory
        final int[] buffersToDelete = new int[] { aLineColorsBufferIdx, aLinePositionsBufferIdx };
        GLES20.glDeleteBuffers(buffersToDelete.length, buffersToDelete, 0);
    }

}