package speiger.src.coreengine.rendering.utils;

import java.nio.IntBuffer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL44;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.coreengine.rendering.shader.ShaderTracker;
import speiger.src.coreengine.rendering.shader.uniform.GlobalUniforms;
import speiger.src.coreengine.rendering.textures.base.TextureTracker;
import speiger.src.coreengine.rendering.utils.states.BlendState;
import speiger.src.coreengine.rendering.utils.states.CullState;
import speiger.src.coreengine.rendering.utils.states.FloatState;
import speiger.src.coreengine.rendering.utils.states.GLProvoking;
import speiger.src.coreengine.rendering.utils.states.GLState;
import speiger.src.coreengine.rendering.utils.states.GLWireFrame;
import speiger.src.coreengine.rendering.utils.states.IGLState;
import speiger.src.coreengine.rendering.utils.states.PixelState;
import speiger.src.coreengine.rendering.utils.states.TextureState;
import speiger.src.coreengine.rendering.utils.values.GLCullType;
import speiger.src.coreengine.utils.counters.averager.Counter;

public class GLStateTracker {
	private static final ThreadLocal<GLStateTracker> TRACKER = ThreadLocal.withInitial(GLStateTracker::new);
	final ObjectList<IGLState> all_states = new ObjectArrayList<>();
	//GPU States
	public final GLState multisampling = addState(new GLState(GL13.GL_MULTISAMPLE));
	public final GLWireFrame wireframe = addState(new GLWireFrame(GLCullType.BOTH));
	public final GLState provoking_vertex = addState(new GLProvoking());
	public final GLState debth_test = addState(new GLState(GL11.GL_DEPTH_TEST));
	public final CullState cull_face = addState(new CullState(GLCullType.BACK));
	public final BlendState blend = addState(new BlendState());
	public final GLState[] clip_planes = addStates(new GLState(GL11.GL_CLIP_PLANE0), new GLState(GL11.GL_CLIP_PLANE1), new GLState(GL11.GL_CLIP_PLANE2), new GLState(GL11.GL_CLIP_PLANE3), new GLState(GL11.GL_CLIP_PLANE4), new GLState(GL11.GL_CLIP_PLANE5));
	public final FloatState point_size = addState(new FloatState(1F, GL11::glPointSize));
	public final FloatState line_size = addState(new FloatState(1F, GL11::glLineWidth));
	public final ScissorsManager scissors = new ScissorsManager(100);
	public final Counter[] counters = Counter.createCounters(4);
	public final TextureState textures = addState(new TextureState());
	public final ViewPortStack viewPort = new ViewPortStack();
	
	//Texture States
	public final PixelState unpack_alignment = new PixelState(GL11.GL_UNPACK_ALIGNMENT, 4);
	public final PixelState unpack_skip_pixel = new PixelState(GL11.GL_UNPACK_SKIP_PIXELS, 0);
	public final PixelState unpack_skip_rows = new PixelState(GL11.GL_UNPACK_SKIP_ROWS, 0);
	public final PixelState unpack_row_length = new PixelState(GL11.GL_UNPACK_ROW_LENGTH, 0);
	
	//Trackers
	public final ShaderTracker shaders = new ShaderTracker();
	public static final TextureTracker TEXTURE_TRACKER = new TextureTracker();
	public final GlobalUniforms uniforms = new GlobalUniforms();
	
	public static GLStateTracker instance() {
		return TRACKER.get();
	}
	
	public <T extends IGLState> T addState(T state) {
		all_states.add(state);
		return state;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends IGLState> T[] addStates(T... states) {
		all_states.addAll(states);
		return states;
	}
	
	public void reapplyState() {
		all_states.forEach(IGLState::reapply);
	}
	
	public void onFrameEnded() {
		for(int i = 0,m = counters.length;i < m;i++) {
			counters[i].onFinished();
		}
	}
	
	public static void drawArrays(int mode, int count) {
		drawArrays(mode, 0, count);
	}
	
	public static void drawArrays(int mode, int first, int count) {
		addVerties(mode, count);
		GL11.glDrawArrays(mode, first, count);
	}
	
	public static void drawElements(int mode, int count, int type, long indices) {
		addVerties(mode, count);
		GL11.glDrawElements(mode, count, type, indices);
	}
	
	public static void drawElements(int mode, IntBuffer buffer) {
		addVerties(mode, buffer.remaining());
		GL11.glDrawElements(mode, buffer);
	}
	
	public static void drawArraysInstanced(int mode, int first, int count, int instanced) {
		addVerties(mode, count * instanced);
		GL31.glDrawArraysInstanced(mode, first, count, instanced);
	}
	
	public static void drawElementsInstanced(int mode, int count, int type, long indices, int instanced) {
		addVerties(mode, count * instanced);
		GL31.glDrawElementsInstanced(mode, count, type, indices, instanced);
	}
	
	public static void drawElementsIndirect(int mode, int type, long pointer, int drawCount, int stride) {
		addVerties(mode, drawAmount(pointer, drawCount));
		GL44.glMultiDrawElementsIndirect(mode, type, pointer, drawCount, stride);
	}
	
	static int drawAmount(long pointer, int amount) {
		int total = 0;
		return total;
	}
	
	static void addVerties(int mode, int count) {
//		counters[0].add(count);
//		switch(mode) {
//			case GL11.GL_POINTS:
//				counters[1].add(count);
//				break;
//			case GL11.GL_LINES:
//				counters[2].add(count / 2);
//				break;
//			case GL11.GL_LINE_LOOP:
//				counters[2].add((count + 2) / 2);
//				break;
//			case GL11.GL_LINE_STRIP:
//				counters[2].add(count - 1);
//				break;
//			case GL11.GL_TRIANGLES:
//				counters[3].add(count / 3);
//				break;
//			case GL11.GL_TRIANGLE_STRIP:
//				counters[3].add(count - 2);
//				break;
//			case GL11.GL_TRIANGLE_FAN:
//				counters[3].add(count - 2);
//				break;
//		}
	}
}
