package openmods.renderer.shaders;

import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLContext;

public class ShaderHelper {
	/**
	 * Class for shader functions, calling ARB methods if GL20 is not supported
	 * A lot of this is already implemented in OpenGlHelper, but it misses some
	 * methods and is confusing in general.
	 */

	static {
		initialize();
	}

	public static IShaderMethods methods;

	static void initialize() {
		ContextCapabilities caps = GLContext.getCapabilities();

		if (GL20ShaderMethods.isSupported(caps)) methods = new GL20ShaderMethods();
		else if (ARBShaderMethods.isSupported(caps)) methods = new ARBShaderMethods();
	}

	public static boolean isSupported() {
		return methods != null;
	}

	public static IShaderMethods methods() {
		return methods;
	}

	public static interface IShaderMethods {
		public int glCreateProgram();

		public int glCreateShader(int type);

		public void glAttachShader(int program, int shader);

		public void glDetachShader(int program, int shader);

		public void glLinkProgram(int program);

		public void glValidateProgram(int program);

		public void glDeleteProgram(int program);

		public void glDeleteShader(int shader);

		public int glGetProgrami(int shader, int parameter);

		public int glGetShaderi(int program, int parameter);

		public String getProgramLogInfo(int program);

		public String getShaderLogInfo(int shader);

		public void glUseProgram(int program);

		public void glShaderSource(int shader, String shaderSource);

		public void glCompileShader(int shader);

		public int glGetUniformLocation(int program, String uniform);

		public void glUniform1i(int loc, int val);

		public void glUniform1f(int loc, float val);

		public void glUniform3f(int loc, float x, float y, float z);

		public int glGetAttribLocation(int program, String attrib);

		public void glEnableVertexAttribArray(int index);

		public void glDisableVertexAttribArray(int index);

		public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset);
	}

	private static class GL20ShaderMethods implements IShaderMethods {
		public static boolean isSupported(ContextCapabilities caps) {
			return caps.OpenGL20;
		}

		@Override
		public int glCreateProgram() {
			return GL20.glCreateProgram();
		}

		@Override
		public int glCreateShader(int type) {
			return GL20.glCreateShader(type);
		}

		@Override
		public void glAttachShader(int program, int shader) {
			GL20.glAttachShader(program, shader);
		}

		@Override
		public void glDetachShader(int program, int shader) {
			GL20.glDetachShader(program, shader);
		}

		@Override
		public void glLinkProgram(int program) {
			GL20.glLinkProgram(program);
		}

		@Override
		public void glValidateProgram(int program) {
			GL20.glValidateProgram(program);
		}

		@Override
		public void glDeleteProgram(int program) {
			GL20.glDeleteProgram(program);
		}

		@Override
		public void glDeleteShader(int shader) {
			GL20.glDeleteShader(shader);
		}

		@Override
		public int glGetProgrami(int program, int parameter) {
			return GL20.glGetProgrami(program, parameter);
		}

		@Override
		public int glGetShaderi(int shader, int parameter) {
			return GL20.glGetShaderi(shader, parameter);
		}

		@Override
		public String getProgramLogInfo(int program) {
			return GL20.glGetProgramInfoLog(program, glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH));
		}

		@Override
		public String getShaderLogInfo(int shader) {
			return GL20.glGetShaderInfoLog(shader, glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
		}

		@Override
		public void glUseProgram(int program) {
			GL20.glUseProgram(program);
		}

		@Override
		public void glShaderSource(int shader, String shaderSource) {
			GL20.glShaderSource(shader, shaderSource);
		}

		@Override
		public void glCompileShader(int shader) {
			GL20.glCompileShader(shader);
		}

		@Override
		public int glGetUniformLocation(int program, String uniform) {
			return GL20.glGetUniformLocation(program, uniform);
		}

		@Override
		public void glUniform1i(int loc, int val) {
			GL20.glUniform1i(loc, val);
		}

		@Override
		public void glUniform1f(int loc, float val) {
			GL20.glUniform1f(loc, val);
		}

		@Override
		public void glUniform3f(int loc, float x, float y, float z) {
			GL20.glUniform3f(loc, x, y, z);
		}

		@Override
		public int glGetAttribLocation(int program, String attrib) {
			return GL20.glGetAttribLocation(program, attrib);
		}

		@Override
		public void glEnableVertexAttribArray(int index) {
			GL20.glEnableVertexAttribArray(index);
		}

		@Override
		public void glDisableVertexAttribArray(int index) {
			GL20.glDisableVertexAttribArray(index);
		}

		@Override
		public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
			GL20.glVertexAttribPointer(index, size, type, normalized, stride, offset);
		}
	}

	private static class ARBShaderMethods implements IShaderMethods {
		public static boolean isSupported(ContextCapabilities caps) {
			return caps.GL_ARB_shader_objects && caps.GL_ARB_vertex_shader && caps.GL_ARB_fragment_shader;
		}

		@Override
		public int glCreateProgram() {
			return ARBShaderObjects.glCreateProgramObjectARB();
		}

		@Override
		public int glCreateShader(int type) {
			return ARBShaderObjects.glCreateShaderObjectARB(type);
		}

		@Override
		public void glAttachShader(int program, int shader) {
			ARBShaderObjects.glAttachObjectARB(program, shader);
		}

		@Override
		public void glDetachShader(int program, int shader) {
			ARBShaderObjects.glDetachObjectARB(program, shader);
		}

		@Override
		public void glLinkProgram(int program) {
			ARBShaderObjects.glLinkProgramARB(program);
		}

		@Override
		public void glValidateProgram(int program) {
			ARBShaderObjects.glValidateProgramARB(program);
		}

		@Override
		public void glDeleteProgram(int program) {
			ARBShaderObjects.glDeleteObjectARB(program);
		}

		@Override
		public void glDeleteShader(int shader) {
			ARBShaderObjects.glDeleteObjectARB(shader);
		}

		@Override
		public int glGetProgrami(int program, int parameter) {
			return ARBShaderObjects.glGetObjectParameteriARB(program, parameter);
		}

		@Override
		public int glGetShaderi(int shader, int parameter) {
			return ARBShaderObjects.glGetObjectParameteriARB(shader, parameter);
		}

		@Override
		public String getProgramLogInfo(int program) {
			return ARBShaderObjects.glGetInfoLogARB(program, glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH));
		}

		@Override
		public String getShaderLogInfo(int shader) {
			return ARBShaderObjects.glGetInfoLogARB(shader, glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
		}

		@Override
		public void glUseProgram(int program) {
			ARBShaderObjects.glUseProgramObjectARB(program);
		}

		@Override
		public void glShaderSource(int shader, String shaderSource) {
			ARBShaderObjects.glShaderSourceARB(shader, shaderSource);
		}

		@Override
		public void glCompileShader(int shader) {
			ARBShaderObjects.glCompileShaderARB(shader);
		}

		@Override
		public int glGetUniformLocation(int program, String uniform) {
			return ARBShaderObjects.glGetUniformLocationARB(program, uniform);
		}

		@Override
		public void glUniform1i(int loc, int val) {
			ARBShaderObjects.glUniform1iARB(loc, val);
		}

		@Override
		public void glUniform1f(int loc, float val) {
			ARBShaderObjects.glUniform1fARB(loc, val);
		}

		@Override
		public void glUniform3f(int loc, float x, float y, float z) {
			ARBShaderObjects.glUniform3fARB(loc, x, y, z);
		}

		@Override
		public int glGetAttribLocation(int program, String attrib) {
			return ARBVertexShader.glGetAttribLocationARB(program, attrib);
		}

		@Override
		public void glEnableVertexAttribArray(int index) {
			ARBVertexShader.glEnableVertexAttribArrayARB(index);
		}

		@Override
		public void glDisableVertexAttribArray(int index) {
			ARBVertexShader.glDisableVertexAttribArrayARB(index);
		}

		@Override
		public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
			ARBVertexShader.glVertexAttribPointerARB(index, size, type, normalized, stride, offset);
		}
	}
}