package freed.gl.program;

import android.opengl.GLES31;

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

import freed.gl.shader.Shader;
import freed.gl.texture.GLFrameBuffer;
import freed.gl.texture.GLTex;
import freed.utils.Log;

public abstract class GLDrawProgram extends GLProgram implements GLDrawProgramInterface{

    private final String TAG = GLDrawProgram.class.getSimpleName();
    private Shader vertexShader;
    private Shader fragmentShader;
    private final float[] vtmp = {1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
    private final float[] ttmp = {1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
    protected FloatBuffer vertexBuffer;
    protected FloatBuffer textureBuffer;
    protected int vTexCoord;
    protected int vPosition;
    protected int sTexture;

    public GLDrawProgram(float glesVersion) {
        super(glesVersion);
    }

    @Override
    public void create(Shader vertexShader, Shader fragmentShader) {
        vertexBuffer = ByteBuffer.allocateDirect(vtmp.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        vertexBuffer.put(vtmp);
        vertexBuffer.position(0);
        textureBuffer = ByteBuffer.allocateDirect(ttmp.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        textureBuffer.put(ttmp);
        textureBuffer.position(0);
        this.vertexShader = vertexShader;
        this.fragmentShader = fragmentShader;
        createAndLinkProgram();
    }

    @Override
    public void createAndLinkProgram() {
        super.createAndLinkProgram();
        checkGlError("glCreateProgram");
        Log.d(TAG,"createAndLinkProgram id:" +hProgram);
        if (vertexShader != null) {
            GLES31.glAttachShader(hProgram, vertexShader.getHandel());
            checkGlError("glAttachShader vertex");
        }
        if (fragmentShader != null) {
            GLES31.glAttachShader(hProgram, fragmentShader.getHandel());
            checkGlError("glAttachShader fragment");
        }
        GLES31.glLinkProgram(hProgram);
        checkGlError("glLinkProgram");
        vPosition = GLES31.glGetAttribLocation(hProgram, "vPosition");
        vTexCoord = GLES31.glGetAttribLocation(hProgram, "vTexCoord");
    }

    @Override
    public void draw(GLTex input, GLFrameBuffer output) {

        if (output != null)
            output.setActive();
        else
            GLFrameBuffer.switchToDefaultFB();
        //step0 clear
        onClear();
        //step1 use program
        onUseProgram();
        //step2 active and bind custom data
        onSetData();
        //step3 bind texture
        onBindTexture(input);
        //step4 normal draw
        onDraw();
    }

    protected void onDraw()
    {
        GLES31.glEnableVertexAttribArray(vPosition);
        GLES31.glEnableVertexAttribArray(vTexCoord);

        GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);

        GLES31.glDisableVertexAttribArray(vPosition);
        GLES31.glDisableVertexAttribArray(vTexCoord);
    }

    protected void onBindTexture(GLTex input) {
        GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
        checkGlError("onBindTexture glActiveTexture");
        if (input != null) {
            GLES31.glBindTexture(input.getGLTextureType(), input.getId());
            checkGlError("onBindTexture glBindTexture");
        }
        checkGlError("onBindTexture glUniform1i");
    }

    protected void onSetData()
    {
        GLES31.glVertexAttribPointer(vPosition, 2, GLES31.GL_FLOAT, false, 4 * 2, vertexBuffer);
        checkGlError("onSetData vertex Buffer");
        GLES31.glVertexAttribPointer(vTexCoord, 2, GLES31.GL_FLOAT, false, 4 * 2, textureBuffer);
        checkGlError("onSetData texture Buffer");
    }

    public void onUseProgram() {
        GLES31.glUseProgram(hProgram);
        checkGlError("onUseProgram");
    }

    protected void onClear()
    {
        GLES31.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        checkGlError("onClear glClearColor");
        GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT | GLES31.GL_DEPTH_BUFFER_BIT);
        checkGlError("onClear glClear");
    }
}
