package speiger.src.coreengine.rendering.shader.uniform;

import java.util.List;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.vector.floats.Vec2f;
import speiger.src.coreengine.math.vector.floats.Vec3f;
import speiger.src.coreengine.math.vector.floats.Vec4f;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.rendering.shader.ShaderProgram;
import speiger.src.coreengine.rendering.shader.uniform.base.BoolUniform;
import speiger.src.coreengine.rendering.shader.uniform.base.BufferUniform;
import speiger.src.coreengine.rendering.shader.uniform.base.FloatUniform;
import speiger.src.coreengine.rendering.shader.uniform.base.IntUniform;
import speiger.src.coreengine.rendering.shader.uniform.base.TextureUniform;
import speiger.src.coreengine.rendering.shader.uniform.vec.Matrix4fUniform;
import speiger.src.coreengine.rendering.shader.uniform.vec.Vec2fUniform;
import speiger.src.coreengine.rendering.shader.uniform.vec.Vec3fUniform;
import speiger.src.coreengine.rendering.shader.uniform.vec.Vec4fUniform;
import speiger.src.coreengine.rendering.utils.GLStateTracker;

public class UniformManager {
	List<IUniform> uniforms = new ObjectArrayList<>();
	List<IAutoUniform> autoUniforms = new ObjectArrayList<>();
	ShaderProgram owner;
	
	public UniformManager(ShaderProgram owner) {
		this.owner = owner;
	}
	
	public <T extends IUniform> T addGlobalUniform(String name) {
		return addUniform(GLStateTracker.instance().uniforms.get(name));
	}
	
	public <T extends IUniform> T addGlobalUniform(String name, T defaultValue) {
		return addUniform(GLStateTracker.instance().uniforms.getOrDefault(name, defaultValue));
	}
	
	public BoolUniform addBool(String name, boolean defaultValue) { 
		return addUniform(new BoolUniform(name, defaultValue));
	}
	
	public IntUniform addInt(String name, int defaultValue)  {
		return addUniform(new IntUniform(name, defaultValue));
	}
	
	public FloatUniform addFloat(String name, float defaultValue) { 
		return addUniform(new FloatUniform(name, defaultValue));
	}
	
	public Vec2fUniform addVec2(String name, Vec2f defaultValue) {
		return addUniform(new Vec2fUniform(name, defaultValue.copyAsMutable()));
	}
	
	public Vec3fUniform addVec3(String name, Vec3f defaultValue) {
		return addUniform(new Vec3fUniform(name, defaultValue.copyAsMutable()));
	}
	
	public Vec4fUniform addVec4(String name, Vec4f defaultValue) {
		return addUniform(new Vec4fUniform(name, defaultValue.copyAsMutable()));
	}
	
	public Matrix4fUniform addMat(String name, Matrix4f defaultValue) {
		return addUniform(new Matrix4fUniform(name, new Matrix4f(defaultValue)));
	}
	
	public BufferUniform addBuffer(String name, int slot) { 
		return addUniform(new BufferUniform(name, slot));
	}
	
	public TextureUniform addTexture(String name, int unit) {
		return addUniform(new TextureUniform(name, unit));
	}
	
	public <T extends IUniform> T addUniform(T uniform) {
		uniforms.add(uniform);
		uniform.registerShader(owner);
		if(uniform instanceof IAutoUniform auto) {
			autoUniforms.add(auto);
		}
		return uniform;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends IUniform> T getUniform(String name) {
		for(int i = 0,m=uniforms.size();i<m;i++) {
			IUniform uni = uniforms.get(i);
			if(uni.name().equals(name)) return (T)uni;
		}
		return null;
	}
	
	public void validate() {
		for(int i = 0,m=uniforms.size();i<m;i++) {
			uniforms.get(i).registerShader(owner);
		}
	}
	
	public void bind() {
		if(autoUniforms.isEmpty()) return;
		for(int i = 0,m=autoUniforms.size();i<m;i++) {
			autoUniforms.get(i).bind(owner.id());
		}
	}
	
	public void remove() {
		for(int i = 0,m=uniforms.size();i<m;i++) {
			uniforms.get(i).removeShader(owner);
		}
	}
} 