package speiger.src.coreengine.rendering.textures.base;

import java.util.function.IntConsumer;

import speiger.src.collections.longs.lists.LongArrayList;
import speiger.src.collections.longs.lists.LongList;
import speiger.src.coreengine.math.bits.BitUtil;
import speiger.src.coreengine.rendering.utils.GLFunctions;
import speiger.src.coreengine.rendering.utils.values.IGLValue;
import speiger.src.coreengine.rendering.utils.values.IGLValue.IGLDataType;
import speiger.src.coreengine.rendering.utils.values.IGLValue.ITextureFormatType;
import speiger.src.coreengine.rendering.utils.values.IGLValue.ITextureParameter;

public class TextureMetadata {
	int internalFormat;
	int externalFormat;
	int dataFormat;
	boolean isSTBData = false;
	boolean mipmapping = false;
	LongList arguments = new LongArrayList();
	IntConsumer specialarguments;
	
	private TextureMetadata() {}
	
	public void applyArguments(int texture) { 
		for(int i = 0,m=arguments.size();i<m;i++) {
			long value = arguments.getLong(i);
			GLFunctions.textureParameter(texture, BitUtil.intKey(value), BitUtil.intValue(value));
		}
		if(specialarguments != null) {
			specialarguments.accept(texture);
		}
	}
	
	public LongList getArguments() {
		return arguments.unmodifiable();
	}
	
	public int getInternalFormat() { return internalFormat; }
	public int getExternalFormat() { return externalFormat; }
	public int getDataFormat() { return dataFormat; }
	public boolean isSTBData() { return isSTBData; }
	public boolean generateMipMappings() { return mipmapping; }
	public Builder copy() { return new Builder(this); }
	public static Builder builder() { return new Builder(); }
	
	public static class Builder {
		TextureMetadata metadata = new TextureMetadata();
		
		private Builder() {}
		
		private Builder(TextureMetadata data) {
			metadata.internalFormat = data.internalFormat;
			metadata.externalFormat = data.externalFormat;
			metadata.dataFormat = data.dataFormat;
			metadata.isSTBData = data.isSTBData;
			metadata.mipmapping = data.mipmapping;
			metadata.arguments.addAll(data.arguments);
			metadata.specialarguments = data.specialarguments;
		}
		
		public Builder internalFormat(ITextureFormatType type) {
			if(!type.internal()) throw new IllegalArgumentException("The GL_Type ["+type.glValue()+"] isn't supported in the internal format");
			return internalFormat(type.glValue());
		}
		
		public Builder internalFormat(int glValue) {
			metadata.internalFormat = glValue;
			return this;
		}
		
		public Builder externalFormat(ITextureFormatType type) {
			return externalFormat(type.glValue());
		}
		
		public Builder externalFormat(int glValue) {
			metadata.externalFormat = glValue;
			return this;
		}
		
		public Builder dataFormat(IGLDataType type) {
			return dataFormat(type.glValue());
		}
		
		public Builder dataFormat(int glValue) {
			metadata.dataFormat = glValue;
			return this;
		}
		
		public Builder stbData(boolean value) {
			metadata.isSTBData = value;
			return this;
		}
		
		public Builder mipmapping(boolean value) {
			metadata.mipmapping = value;
			return this;
		}
		
		public Builder removeArgument(ITextureParameter id) {
			return removeArgument(id.glValue());
		}
		
		public Builder removeArgument(int id) {
			LongList list = metadata.arguments;
			for(int i = 0,m=list.size();i<m;i++) {
				if(BitUtil.intKey(list.getLong(i)) == id) {
					list.removeLong(i--);
					m--;
				}
			}
			return this;
		}
		
		public Builder arguement(ITextureParameter parameter, IGLValue value) {
			return argument(parameter, value.glValue());
		}
		
		public Builder argument(ITextureParameter parameter, int value) {
			if(!parameter.isValid(value)) throw new IllegalArgumentException("Value ["+value+"] isn't supported for GL Function ["+parameter.glValue()+"], only int functions are supported with this");
			return argument(parameter.glValue(), value);
		}
		
		public Builder argument(int id, int value) {
			metadata.arguments.add(BitUtil.toLong(id, value));
			return this;
		}
		
		public Builder specialarguments(IntConsumer command) {
			metadata.specialarguments = command;
			return this;
		}
		
		public TextureMetadata build() {
			TextureMetadata meta = metadata;
			metadata = null;
			return meta;
		}
	}
}
