package speiger.src.coreengine.platform.graphics.api.vertex;

import java.util.Objects;

import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.objects.collections.ObjectIterable;
import speiger.src.collections.objects.collections.ObjectIterator;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.utils.ObjectIterators;
import speiger.src.coreengine.platform.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element;

public class VertexLayout implements ObjectIterable<Element> {
	Object2ObjectMap<String, Element> mapped = Object2ObjectMap.builder().linkedMap();
	ObjectList<Element> elements = new ObjectArrayList<>();
	IntList offsets = new IntArrayList();
	int stride;
	int bytes;
	
	private VertexLayout() {}
	
	public static Builder builder() {
		return new Builder(new VertexLayout());
	}
	
	public int stride() { return stride; }
	public int bytes() { return bytes; }
	public boolean isEmpty() { return elements.isEmpty(); }
	public int size() { return elements.size(); }
	public int offset(int index) { return offsets.getInt(index); }
	public Element get(int index) { return elements.get(index); }
	public boolean contains(String key) { return mapped.containsKey(key); }
	public ObjectList<Element> elements() { return elements.unmodifiable(); }
	@Override
	public ObjectIterator<Element> iterator() { return ObjectIterators.unmodifiable(elements.iterator()); }
	
	private void add(Element element) {
		if(mapped.put(element.name(), element) != null) throw new IllegalStateException("Duplicated Names are not allowed");
		elements.add(element);
		offsets.add(bytes);
		int size = element.size();
		stride += size;
		bytes += element.type().size(size);
	}
	
	public static class Builder {
		VertexLayout format;

		private Builder(VertexLayout format) {
			this.format = format;
		}
		
	    public Builder element(String name, int index, int size, Usage usage) {
	        return element(name, index, size, usage, GraphicsDataType.FLOAT, false);
	    }

	    public Builder element(String name, int index, int size, Usage usage, GraphicsDataType type) {
	        return element(name, index, size, usage, type, false);
	    }
		
		public Builder element(String name, int index, int size, Usage usage, GraphicsDataType type, boolean normalized) {
			format.add(new Element(name, index, size, usage, type, normalized));
			return this;
		}
		
	    public Builder array(String baseName, int startIndex, int width, int size, Usage usage, GraphicsDataType type) {
	        return array(baseName, startIndex, width, size, usage, type, false);
	    }
		
		public Builder array(String baseName, int startIndex, int width, int size, Usage usage, GraphicsDataType type, boolean normalized) {
			Objects.requireNonNull(baseName);
			for(int i = 0;i<width;i++) {
				element(baseName+" ["+i+"]", startIndex + i, size, usage, type, normalized);
			}
			return this;
		}
		
		public VertexLayout build() {
			return format;
		}
	}
	
	public record Element(String name, int index, int size, Usage usage, GraphicsDataType type, boolean normalized) {}
	
	public enum Usage {
		POS, UV, COLOR, NORMAL, MATRIX, CUSTOM; 
	}
}
