package de.frajul.endlessroll.rendering.shader;

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

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import de.frajul.endlessroll.main.GameLog;

/**
 * Created by Julian on 23.11.2015.
 */
public abstract class ShaderProgram {

    private Context context;
    private int vertexShader, fragmentShader, program;

    public ShaderProgram(Context context, String vertexShaderPath, String fragmentShaderPath) throws Exception {
        this.context = context;
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderPath);
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderPath);
        program = GLES20.glCreateProgram();
        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);
        GLES20.glLinkProgram(program);
        int[] linkStatus = new int[1];
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] == GLES20.GL_FALSE) {
            GLES20.glDeleteProgram(program);
            throw new Exception("Could not link program: "
                    + GLES20.glGetProgramInfoLog(program));
        }

        bindAttribLocations();
        loadUniformLocations();
        GameLog.d("ShaderProgram successfully loaded");
    }

    private void bindAttribLocations() {
        GLES20.glBindAttribLocation(program, 0, "position");
        GLES20.glBindAttribLocation(program, 1, "texCoords");
    }

    protected abstract void loadUniformLocations();

    protected int getUniformLocation(String name) {
        return GLES20.glGetUniformLocation(program, name);
    }

    public void start() {
        GLES20.glUseProgram(program);
    }

    public void stop() {
        GLES20.glUseProgram(0);
    }


    private int loadShader(int type, String shaderName) throws Exception {
        try {
            InputStream is = context.getAssets().open(shaderName);
            InputStreamReader isReader = new InputStreamReader(is);
            BufferedReader reader = new BufferedReader(isReader);
            StringBuilder source = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null)
                source.append(line);

            int shader = GLES20.glCreateShader(type);
            GLES20.glShaderSource(shader, source.toString());
            GLES20.glCompileShader(shader);

            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == GLES20.GL_FALSE) {
                GLES20.glDeleteShader(shader);
                throw new Exception("Could not compile shader \"" + shaderName + "\": "
                        + GLES20.glGetShaderInfoLog(shader));
            }
            GameLog.d("Shader \"" + shaderName + "\" successfully loaded");
            return shader;
        } catch (Exception e) {
            throw new Exception("Could not load Shader \"" + shaderName + "\"", e);
        }
    }

}
