package speiger.src.coreengine.rendering.gui.renderer;

import java.util.List;
import java.util.function.Consumer;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.math.vector.floats.Vec4f;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.math.vector.matrix.Matrix4fStack;
import speiger.src.coreengine.math.vector.quaternion.Quaternion;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.models.DrawCall;
import speiger.src.coreengine.rendering.tesselation.buffer.VertexBuilder;
import speiger.src.coreengine.rendering.tesselation.format.VertexFormat;
import speiger.src.coreengine.rendering.tesselation.format.VertexTypes;
import speiger.src.coreengine.rendering.utils.ScissorsManager;
import speiger.src.coreengine.rendering.utils.values.GLMode;

public class SimpleUIRenderer implements IUIRenderer, AutoCloseable {
	TexturedRect cachedRect = new TexturedRect();
	Matrix4fStack transform = new Matrix4fStack();
	List<Matrix4f> transformCache = new ObjectArrayList<>(new Matrix4f());
	int currentMatrix = 0;
	int lastTexture = 0;
	List<UIDrawCall> drawCalls = new ObjectArrayList<>();
	VertexBuilder builder = new VertexBuilder(Short.MAX_VALUE);
	ScissorsManager manager = new ScissorsManager(32);
	Window window;
	
	
	public SimpleUIRenderer(Window window) {
		this.window = window;
	}

	@Override
	public void close() throws Exception {
		builder.close();
	}
	
	@Override
	public boolean isInScissors(IGuiBox box) {
		return manager.contains(box);
	}
	
	@Override
	public void pushScissors(IGuiBox box) {
		float minX = box.getMinX();
		float minY = box.getMinY();
		manager.push((int)minX, (int)minY, (int)(box.getMaxX() - minX), (int)(box.getMaxY() - minY));
	}
	
	@Override
	public void popScissors() {
		manager.pop();
	}
	
	@Override
	public TexturedRect getCachedRect() { return cachedRect; }
	
	@Override
	public void flush() {
		pushDrawcall();
		
	}
	
	@Override
	public void pushTransform() {
		transform.push();
		invalidateMatrix();
	}
	
	@Override
	public void popTransform() {
		transform.pop();
		invalidateMatrix();
	}
	
	@Override
	public void translate(float z) {
		transform.translate(0, 0, z);
		invalidateMatrix();
	}
	
	@Override
	public void translate(float x, float y) {
		transform.translate(x, y);
		invalidateMatrix();
	}
	
	@Override
	public void translate(float x, float y, float z) {
		transform.translate(x, y, z);
		invalidateMatrix();
	}
	
	@Override
	public void scale(float scale) {
		transform.scale(scale);
		invalidateMatrix();
	}
	
	@Override
	public void scale(float x, float y) {
		transform.scale(x, y, 1F);
		invalidateMatrix();
	}
	
	@Override
	public void rotate(Quaternion rotation) {
		transform.rotate(rotation);
		invalidateMatrix();
	}
	
	protected void invalidateMatrix() {
		pushDrawcall();
		currentMatrix = -1;
	}
	
	protected void validateMatrix() {
		if(currentMatrix != -1) return;
		for(int i = transformCache.size();i>=0;i--) {
			if(transformCache.get(i).equals(transform)) {
				currentMatrix = i;
				return;
			}
		}
		currentMatrix = transformCache.size();
		transformCache.add(transform.copyCurrent());
	}
	
	protected void ensureDrawCall(GLMode mode, VertexFormat format, int texture) {
		if(!builder.is(mode, format) || lastTexture != texture) {
			pushDrawcall();
			lastTexture = texture;
			builder.start(mode, format);
		}
	}
	
	private void pushDrawcall() {
		if(builder.hasData()) {
			int start = builder.size();
			drawCalls.add(new UIDrawCall(builder.getMode(), start, builder.last()-start, lastTexture, currentMatrix, Vec4f.ZERO, 0F));
			builder.endDrawCall(false);
		}
	}
	
	@Override
	public void drawCustom(Consumer<Matrix4f> matrix) {
		validateMatrix();
	}
	
	@Override
	public void drawCustom(VertexFormat format, DrawCall drawcall) {
		validateMatrix();
		ensureDrawCall(drawcall.glType(), format, drawcall.textureId());
		builder.putBulk(drawcall.data());
	}
	
	@Override
	public void drawLine(float minX, float minY, float maxX, float maxY, float zLevel, int color) {
		validateMatrix();
		ensureDrawCall(GLMode.TRIANGLES, VertexTypes.POS_RGBA, 0);
	}
	
	@Override
	public void drawFrame(float minX, float minY, float maxX, float maxY, float zLevel, int color) {
		validateMatrix();
		ensureDrawCall(GLMode.QUADS, VertexTypes.POS_RGBA, 0);
	}
	
	@Override
	public void drawRect(float minX, float minY, float maxX, float maxY, float zLevel, int color) {
		validateMatrix();
		ensureDrawCall(GLMode.QUADS, VertexTypes.POS_RGBA, 0);
	}
	
	@Override
	public void drawGradientRect(float minX, float minY, float maxX, float maxY, float zLevel, int startColor, int endColor, Facing facing) {
		validateMatrix();
		ensureDrawCall(GLMode.QUADS, VertexTypes.POS_RGBA, 0);
	}
	
	@Override
	public void drawTexturedRect(float minX, float minY, float maxX, float maxY, float zLevel, int texture, float minU, float minV, float maxU, float maxV, int color) {
		validateMatrix();
		ensureDrawCall(GLMode.QUADS, VertexTypes.POS_TEX_RGBA, texture);
	}
}