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

import java.util.Objects;
import java.util.OptionalInt;

import org.jspecify.annotations.Nullable;

import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.platform.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.platform.graphics.api.utils.GraphicsResource;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout;

public abstract class Mesh implements GraphicsResource {
	protected final Int2ObjectMap<LayoutInfo> layouts;
	protected Int2ObjectMap<VertexBuffer> buffers;
	@Nullable
	protected VertexBuffer indeciesBuffer;
	@Nullable
	protected IndeciesType indeciesType;
	
	public Mesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type) {
		this.layouts = layouts;
		this.buffers = buffers;
		this.indeciesBuffer = indeciesBuffer;
		this.indeciesType = type;
	}
	
	public abstract void closeWithBuffers();
	
	public Int2ObjectMap<LayoutInfo> layouts() {
		return layouts;
	}
	
	@Nullable
	public VertexBuffer indeciesBuffer() {
		return indeciesBuffer;
	}
	
	@Nullable
	public IndeciesType indeciesType() {
		return indeciesType;
	}
	
	public VertexBuffer buffer(int binding) {
		return buffers.get(binding);
	}
	
	public Mesh indexBuffer(@Nullable VertexBuffer indexBuffer, @Nullable IndeciesType indeciesType) {
		this.indeciesBuffer = indexBuffer;
		this.indeciesType = indeciesType;
		return this;
	}
	
	public Mesh buffer(int binding, @Nullable VertexBuffer buffer) {
		buffers.put(binding, buffer);
		return this;
	}
	
	public static Builder builder() {
		return new Builder();
	}
	
	public static class Builder {
		Int2ObjectMap<BufferState> allocateState = new Int2ObjectOpenHashMap<BufferState>().setDefaultReturnValue(BufferState.STATIC_DRAW);
		Int2ObjectMap<LayoutInfo> layouts = Int2ObjectMap.builder().linkedMap();
		IndeciesType type = IndeciesType.INT;
		OptionalInt preallocated = OptionalInt.empty();
		boolean autoGenerate;
		
		private Builder() {}
		
		public Builder autogenerate() {
			autoGenerate = true;
			return this;
		}
		
		public Builder preallocate(int bytes) {
			preallocated = OptionalInt.of(bytes);
			return this;
		}
		
		public Builder indecies(IndeciesType type) {
			type = Objects.requireNonNull(type);
			return this;
		}
		
		public Builder bufferState(BufferState state) {
			allocateState.setDefaultReturnValue(Objects.requireNonNull(state));
			return this;
		}
		
		public Builder bufferState(int index, BufferState state) {
			allocateState.put(index, state);
			return this;
		}
		
		public Builder layout(VertexLayout layout) {
			return layout(layouts.size(), layout);
		}
		
		public Builder layout(int index, VertexLayout layout) {
			return layout(index, layout, 0);
		}
		
		public Builder layout(int index, VertexLayout layout, int instanceCount) {
			if(layout.isEmpty()) throw new IllegalStateException("Empty Layouts are not valid");
			layouts.put(index, new LayoutInfo(Objects.requireNonNull(layout), instanceCount));
			return this;
		}
		
		public void validate() {
			if(layouts.isEmpty()) throw new IllegalStateException("Layouts have to be provided");
		}
		
		public boolean isAutogenerated() {
			return autoGenerate;
		}
		
		public IndeciesType indecies() {
			return type;
		}
		
		public OptionalInt preAllocated() {
			return preallocated;
		}
		
		public BufferState getState(int index) {
			return allocateState.get(index);
		}
		
		public Int2ObjectMap<LayoutInfo> info() {
			return layouts.unmodifiable();
		}
	}
	
	public record LayoutInfo(VertexLayout layout, int instanceCount) {}
}
