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

import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.List;

import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.rendering.tesselation.commands.DrawElementsIndirectCommand;
import speiger.src.coreengine.rendering.utils.values.GLBuffer;

public class IndirectElementBuffer extends VertexBuffer implements Iterable<DrawElementsIndirectCommand> {
	List<DrawElementsIndirectCommand> commands = new ObjectArrayList<>();
	boolean managed = false;
	
	public IndirectElementBuffer() {
		super(GLBuffer.DRAW_INDIRECT_BUFFER);
	}
	
	public IndirectElementBuffer(int size) {
		super(GLBuffer.DRAW_INDIRECT_BUFFER);
		for(int i = 0;i<size;i++) {
			commands.add(new DrawElementsIndirectCommand());
		}
		allocate(size * 20);
	}
	
	public int commands() {
		return commands.size();
	}
	
	public IndirectElementBuffer setManaged(boolean value) {
		managed = value;
		return this;
	}
	
	public IndirectElementBuffer add(DrawElementsIndirectCommand command) {
		commands.add(command);
		upload(command, commands.size()-1);
		return this;
	}
	
	public IndirectElementBuffer set(int index, DrawElementsIndirectCommand command) {
		commands.set(index, command);
		upload(command, index);
		return this;
	}
	
	public DrawElementsIndirectCommand get(int index) {
		return commands.get(index);
	}
	
	public IndirectElementBuffer update(int index) {
		upload(commands.get(index), index);
		return this;
	}
	
	public IndirectElementBuffer remove(int index) {
		commands.remove(index);
		if(index != commands.size()) shift((index + 1) * 20, index*20, (commands.size()-index) * 20);
		return this;
	}
	
	public IndirectElementBuffer uploadAll() {
		ByteBuffer buffer = MemoryUtil.memAlloc(commands.size() * 20);
		for(int i = 0,m=commands.size();i<m;i++) {
			commands.get(i).write(buffer);
		}
		buffer.flip();
		if(size < commands.size() * 20) set(buffer);
		else fill(0, buffer);
		return this;
	}
	
	private void upload(DrawElementsIndirectCommand command, int index) {
		if(managed) return;
		if(size <= index * 20) grow(size * 2);
		try(MemoryStack stack = MemoryStack.stackPush()) {
			ByteBuffer buf = stack.malloc(20);
			command.write(buf);
			fill(index*20, buf.flip());
		}
	}
	
	@Override
	public Iterator<DrawElementsIndirectCommand> iterator() {
		return commands.iterator();
	}
}