package speiger.src.coreengine.rendering.utils;

import java.nio.IntBuffer;
import java.util.List;

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.coreengine.rendering.utils.states.BlendState;
import speiger.src.coreengine.rendering.utils.states.CullState;
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.LineSize;
import speiger.src.coreengine.rendering.utils.states.PointSize;
import speiger.src.coreengine.utils.counters.averager.Counter;

public class GLUtils
{
	static final List<IGLState> ALL_STATES = new ObjectArrayList<>();
	public static final GLState MULTI_SAMPLING = addState(new GLState(GL13.GL_MULTISAMPLE, false));
	public static final GLState WIRE_FRAME = addState(new GLWireFrame());
	public static final GLState PROVOKING_VERTEX = addState(new GLProvoking());
	public static final GLState DEBTH_TEST = addState(new GLState(GL11.GL_DEPTH_TEST, false));
	public static final CullState CULL_FACE = addState(new CullState(GL11.GL_BACK));
	public static final BlendState BLEND = addState(new BlendState());
	public static final GLState CLIP_PLANE0 = addState(new GLState(GL11.GL_CLIP_PLANE0, false));
	public static final PointSize POINT_SIZE = addState(new PointSize());
	public static final LineSize LINE_SIZE = addState(new LineSize());
	public static final ScissorsManager TESTER = new ScissorsManager(100);
	public static final Counter[] COUNTERS = Counter.createCounters(4);
	public static final ViewPortStack VIEW_PORT = new ViewPortStack();
	
	public static <T extends IGLState> T addState(T state)
	{
		ALL_STATES.add(state);
		return state;
	}
	
	public static void onFrameEnded()
	{
		for(int i = 0,m=COUNTERS.length;i<m;i++)
		{
			COUNTERS[i].onFinished();
		}
		for(int i = 0;i<ALL_STATES.size();i++)
		{
			ALL_STATES.get(i).cleanup();
		}
	}
	
	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;
		}
	}
}
