package speiger.src.coreengine.rendering.tesselationOld;

import java.nio.FloatBuffer;

import speiger.src.coreengine.math.vector.floats.Vec2f;
import speiger.src.coreengine.math.vector.floats.Vec3f;
import speiger.src.coreengine.rendering.models.DrawCall;
import speiger.src.coreengine.rendering.tesselation.format.VertexElement;
import speiger.src.coreengine.rendering.tesselation.format.VertexElement.Usage;
import speiger.src.coreengine.rendering.tesselation.format.VertexFormat;
import speiger.src.coreengine.rendering.utils.values.GLMode;

public class Tesselator implements IVertexBuilder
{
	boolean drawing = false;
	int drawType = 0;
	
	VertexFormat currentList;
	VertexElement currentElement;
	int currentIndex = 0;
	
	Vec3f offset = Vec3f.mutable();
	Vec2f effects = Vec2f.mutable(1F);
	
	FloatBuffer buffer;
	int vertexes = 0;
	
	public Tesselator(int capacity) {
		buffer = FloatBuffer.allocate(capacity);
	}
	
	public Tesselator(FloatBuffer buffer) {
		this.buffer = buffer;
	}
	
	public Tesselator begin(int glType, VertexFormat list) {
		if(drawing) throw new RuntimeException("Already Drawing");
		drawType = glType;
		drawing = true;
		currentList = list;
		currentElement = list.get(0);
		currentIndex = 0;
		buffer.clear();
		vertexes = 0;
		return this;
	}
	
	public Tesselator changeGLType(int glType) {
		if(!drawing) throw new RuntimeException("Can not change type");
		drawType = glType;
		return this;
	}
	
	public Tesselator resetVertexes() {
		vertexes = 0;
		return this;
	}
			
	public Tesselator setBrightness(float value) {
		effects.x(value);
		return this;
	}
	
	public Tesselator setVisibilty(float value) {
		effects.y(value);
		return this;
	}
	
	public float getBrightness() { return effects.x(); }
	public float getVisibility() { return effects.y(); }
	
	public Tesselator resetEffects() {
		effects.set(Vec2f.ONE);
		return this;
	}
	
	public Tesselator offset(float x, float y, float z) {
		offset.add(x, y, z);
		return this;
	}
	
	public Tesselator setOffset(float x, float y, float z) {
		offset.set(x, y, z);
		return this;
	}
	
	@Override
	public Tesselator pos(float x, float y) {
		validateElement(Usage.POS, 2);
		buffer.put(x + offset.x());
		buffer.put(y + offset.y());
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator pos(float x, float y, float z) {
		validateElement(Usage.POS, 3);
		buffer.put(x + offset.x());
		buffer.put(y + offset.y());
		buffer.put(z + offset.z());
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator color3f(float r, float g, float b) {
		validateElement(Usage.COLOR, 3);
		buffer.put(r * effects.x());
		buffer.put(g * effects.x());
		buffer.put(b * effects.x());
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator color4f(float r, float g, float b, float a) {
		validateElement(Usage.COLOR, 4);
		buffer.put(r * effects.x());
		buffer.put(g * effects.x());
		buffer.put(b * effects.x());
		buffer.put(a * effects.y());
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator tex(float u, float v) {
		validateElement(Usage.UV, 2);
		buffer.put(u);
		buffer.put(v);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator normal(float x, float y, float z) {
		validateElement(Usage.NORMAL, 3);
		buffer.put(x);
		buffer.put(y);
		buffer.put(z);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator custom(float x) {
		validateElement(Usage.CUSTOM, 1);
		buffer.put(x);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator custom(float x, float y) {
		validateElement(Usage.CUSTOM, 2);
		buffer.put(x);
		buffer.put(y);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator custom(float x, float y, float z) {
		validateElement(Usage.CUSTOM, 3);
		buffer.put(x);
		buffer.put(y);
		buffer.put(z);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator custom(float x, float y, float z, float w) {
		validateElement(Usage.CUSTOM, 4);
		buffer.put(x);
		buffer.put(y);
		buffer.put(z);
		buffer.put(w);
		nextElement();
		return this;
	}
	
	@Override
	public Tesselator endVertex() {
		vertexes++;
		return this;
	}
	
	public Tesselator finishData() {
		drawing = false;
		buffer.flip();
		return this;
	}
	
	public float[] getData() {
		float[] data = new float[currentList.totalOffset() * vertexes];
		buffer.get(data);
		return data;
	}
	
	@SuppressWarnings("removal")
	public DrawCall getDrawCall(int texture) {
		float[] data = new float[currentList.totalOffset() * vertexes];
		buffer.get(data);
		return new DrawCall(GLMode.byId(drawType), texture, data, vertexes);
	}
	
	public boolean isDrawing() { return drawing; }
	public boolean hasTexture() { return currentList.hasType(Usage.UV); }
	public int getGLDrawType() { return drawType; }
	public int getVertexCount() { return vertexes; }
	
	public void validateElement(Usage usage, int componentSize) {
		if(currentElement.usage() == usage && currentElement.size() == componentSize && drawing) return;
		throw new RuntimeException("Invalid Argument, Drawing: "+drawing+", Element: "+usage+", Expected: "+currentElement.usage()+", Size: "+componentSize+", Expected Size: "+currentElement.size());
	}
	
	protected void nextElement() {
		currentIndex = (currentIndex + 1) % currentList.size();
		currentElement = currentList.get(currentIndex);
	}
}
