package speiger.src.coreengine.rendering.models.buffers;

import java.util.List;

import org.lwjgl.opengl.GL30;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.coreengine.rendering.utils.values.GLBuffer;
import speiger.src.coreengine.rendering.utils.values.GLDataType;

public abstract class VertexArray {
	int id;
	int size;
	ObjectList<BufferAttribute> attributes = new ObjectArrayList<>();
	VertexBuffer indeciesBuffer;
	List<VertexBuffer> buffers = new ObjectArrayList<>();
	
	public VertexArray() {
		id = GL30.glGenVertexArrays();
	}
	
	protected void bind() { GL30.glBindVertexArray(id); }
	
	public void bindArray() {
		bind();
		attributes.forEach(BufferAttribute::bind);
	}
	
	protected BufferAttribute attribute(int index) { return attributes.get(index); }
	
	protected void unbind() { GL30.glBindVertexArray(0); }
	
	public void unbindArray() {
		attributes.forEach(BufferAttribute::unbind);
		unbind();
	}
	
	public int id() { return id; }
	protected void size(int size) { this.size = size; }
	public int size() { return size; }
	public boolean isValid() { return id != -1; }
	public boolean isInvalid() { return id == -1; }
	
	public void remove() {
		if(id == -1) return;
		GL30.glDeleteVertexArrays(id);
		if(indeciesBuffer != null) indeciesBuffer.delete();
		buffers.forEach(VertexBuffer::delete);
		id = -1;
	}
	
	public VertexBuffer indecies() { return indeciesBuffer; }
	public VertexBuffer buffer(int index) { return buffers.get(index); }
	
	public void createIndencies(int[] indencies) { indeciesBuffer = new VertexBuffer(GLBuffer.ELEMENT_BUFFER).bind().set(indencies); }
	public void updateIndencies(int[] indencies) { indeciesBuffer.bind().set(indencies); }
	
	public VertexBuffer createSimpleBuffer(int index, int size, GLDataType type) {
		VertexBuffer buffer = new VertexBuffer(GLBuffer.ARRAY_BUFFER);
		buffers.add(buffer);
		buffer.bind();
		BufferAttribute attribute = new BufferAttribute(index, size, type);
		attributes.add(attribute);
		finishAttributes(attribute);
		return buffer;
	}
	
	public VertexBuffer createBuffer(BufferAttribute... array) {
		VertexBuffer buffer = new VertexBuffer(GLBuffer.ARRAY_BUFFER);
		buffers.add(buffer);
		attributes.addAll(array);
		buffer.bind();
		finishAttributes(array);
		return buffer;
	}
	
	public static void finishAttributes(BufferAttribute... array) {
		finishAttributes(countBytes(array), array);
	}
	
	public static void finishAttributes(int totalBytes, BufferAttribute... array) {
		int offset = 0;
		for(BufferAttribute attr : array) {
			attr.bind();
			attr.applyAttribute(totalBytes, offset);
			offset += attr.type().size(attr.size());
		}
	}
	
	public static int countBytes(BufferAttribute... array) {
		int totalBytes = 0;
		for(BufferAttribute attr : array) {
			totalBytes += attr.type().size(attr.size());
		}
		return totalBytes;
	}
}
