package speiger.src.coreengine.rendering.shaderOld;

import java.util.List;

import org.lwjgl.opengl.GL20;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.base.IAsset;
import speiger.src.coreengine.rendering.shaderOld.ShaderTracker.ReloadReference;
import speiger.src.coreengine.rendering.shaderOld.uniforms.Uniform;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
import speiger.src.coreengine.utils.io.GameLog;

public abstract class ShaderProgram
{
	protected List<Uniform> localUniforms = new ObjectArrayList<>();
	ReloadReference<?> reference;
	private int shaderID;
	
	protected ShaderProgram(AssetLocation vertex, AssetLocation fragment, String[] attributes) {
		int vertexID = loadShader(vertex, GL20.GL_VERTEX_SHADER);
		int fragmentID = loadShader(fragment, GL20.GL_FRAGMENT_SHADER);
		if(vertexID == -1 || fragmentID == -1) {
			shaderID = 0;
			return;
		}
		shaderID = GL20.glCreateProgram();
		GL20.glAttachShader(shaderID, vertexID);
		GL20.glAttachShader(shaderID, fragmentID);
		for(int i = 0;i < attributes.length;i++) {
			GL20.glBindAttribLocation(shaderID, i, attributes[i]);
		}
		GL20.glLinkProgram(shaderID);
		GL20.glDetachShader(shaderID, vertexID);
		GL20.glDetachShader(shaderID, fragmentID);
		GL20.glDeleteShader(vertexID);
		GL20.glDeleteShader(fragmentID);
	}
	
	public void addUniforms(Uniform... uniforms) {
		for(Uniform uni : uniforms) {
			uni.loadUniform(this);
			localUniforms.add(uni);
		}
		GL20.glValidateProgram(shaderID);
	}
	
	void setReference(ReloadReference<?> ref) {
		reference = ref;
	}
	
	public abstract ShaderProgram init();
	
	public boolean isShaderValid() {
		return shaderID != 0;
	}
	
	public int getShaderID() {
		return shaderID;
	}
	
	public void startShader() {
		ShaderTracker.INSTANCE.startNextShader(this);
	}
	
	public void stopShader() {
		ShaderTracker.INSTANCE.stopShader(this);
	}
	
	public boolean isShaderRunning() {
		return ShaderTracker.INSTANCE.activeShader == getShaderID();
	}
	
	public void removeShader() {
		if(shaderID == 0) return;
		stopShader();
		GL20.glDeleteProgram(shaderID);
		shaderID = 0;
		reference.removeReference();
		reference = null;
	}
	
	private int loadShader(AssetLocation location, int type) {
		StringBuilder shaderSource = new StringBuilder();
		try(IAsset asset = ShaderTracker.INSTANCE.getAssets().getAsset(location)) {
			for(String s : IterableWrapper.wrap(asset.reader()))
			{
				if(s.startsWith("//")) continue;
				shaderSource.append(s).append("\n");
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			return -1;
		}
		int shaderID = GL20.glCreateShader(type);
		GL20.glShaderSource(shaderID, shaderSource);
		GL20.glCompileShader(shaderID);
		if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS) == 0) {
			GameLog.warn("Could not compile shader " + location);
			int[] length = new int[1];
			GL20.glGetShaderiv(shaderID, GL20.GL_INFO_LOG_LENGTH, length);
			GameLog.error(GL20.glGetShaderInfoLog(shaderID, length[0]));
			return -1;
		}
		return shaderID;
	}
}
