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

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

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.coreengine.rendering.utils.values.GLDataType;
import speiger.src.coreengine.rendering.utils.values.GLDataType.DataType;
import speiger.src.coreengine.utils.helpers.JsonUtil;

public class VertexLoader
{
	public static List<VertexEntry> loadVertexFormat(JsonObject obj)
	{
		List<VertexEntry> list = new ObjectArrayList<>();
		JsonUtil.iterate(obj.get("dataOrder"), T -> list.add(new VertexEntry(T)));
		return list;
	}
	
	public static ByteBuffer parseVertexData(JsonObject obj, int vertexCount, List<VertexEntry> entries, boolean excludeOptional)
	{
		int byteStride = VertexEntry.caculateByteSize(entries, excludeOptional);
		JsonArray vertexes = obj.getAsJsonArray("vertexes");
		ByteBuffer buffer = ByteBuffer.allocate(vertexCount * byteStride).order(ByteOrder.nativeOrder());
		int stride = VertexEntry.caclulateStride(entries);
		for(int i = 0,offset = 0, byteOffset = 0,m=entries.size();i<m;i++)
		{
			VertexEntry entry = entries.get(i);
			if(!entry.isOptional() || !excludeOptional)
			{
				putIntoBuffer(entry.type(), buffer, new JsonWrapper(vertexes, obj.getAsJsonArray(entry.name()), offset, stride, entry.size(), vertexCount), byteOffset, byteStride, entry.size());
				byteOffset += entry.byteSize();
			}
			offset += entry.size();
		}
		return buffer;
	}
	
	public static Object2ObjectMap<String, List<Number>> parseMappedVertexData(JsonObject obj, List<VertexEntry> entries, boolean excludeOptional)
	{
		Object2ObjectMap<String, List<Number>> mappedData = Object2ObjectMap.builder().linkedMap();
		int stride = VertexEntry.caclulateStride(entries);
		int[] vertexes = JsonUtil.parseIntArray(obj.getAsJsonArray("vertexes"));
		for(int i = 0,offset=0,m=entries.size();i<m;i++)
		{
			VertexEntry entry = entries.get(i);
			if(!entry.isOptional() || !excludeOptional) mappedData.put(entry.name(), deserializeIndecies(vertexes, JsonUtil.parseArray(obj.getAsJsonArray(entry.name())), entry.size(), stride, offset));
			offset += entry.size();
		}
		return mappedData;
	}
	
	public static <T> List<T> deserializeIndecies(int[] indecies, T[] in, int size, int stride, int offset)
	{
		List<T> out = new ObjectArrayList<>();
		for(int i = 0,m=(indecies.length/stride)*size;i<m;i++)
		{
			out.add(in[indecies[offset + ((i/size) * stride) + (i % size)]]);
		}
		return out;
	}
	
	public static interface JsonList
	{
		public JsonElement get(int index);
		public int size();
	}
	
	private static void putIntoBuffer(GLDataType type, ByteBuffer buffer, JsonList list, int offset, int stride, int size)
	{
		int index = offset;
		for(int i = 0,m=list.size();i<m;)
		{
			putIntoBuffer(type.type(), buffer, index, list.get(i));
			index += type.byteSize();
			if(++i % size == 0) {
				index+=stride;
			}
		}
	}
	
	private static void putIntoBuffer(DataType type, ByteBuffer buffer, int index, JsonElement element)
	{
		switch(type)
		{
			case BYTE:
				buffer.put(index, element.getAsByte());
				break;
			case SHORT:
				buffer.putShort(index, element.getAsShort());
				break;
			case INT:
				buffer.putInt(index, element.getAsInt());
				break;
			case LONG:
				buffer.putLong(index, element.getAsLong());
				break;
			case FLOAT:
				buffer.putFloat(index, element.getAsFloat());
				break;
			case DOUBLE:
				buffer.putDouble(index, element.getAsDouble());
				break;
		}
	}
	
	private static class JsonWrapper implements JsonList
	{
		JsonArray array;
		JsonArray vertexes;
		int offset;
		int size;
		int stride;
		int max;
		
		public JsonWrapper(JsonArray vertexes, JsonArray array, int offset, int stride, int size, int max)
		{
			this.array = array;
			this.vertexes = vertexes;
			this.offset = offset;
			this.size = size;
			this.stride = stride;
			this.max = max;
		}
		
		@Override
		public int size()
		{
			return max;
		}
		
		@Override
		public JsonElement get(int index)
		{
			return array.get(vertexes.get(offset + ((index/size) * stride) + (index % size)).getAsInt());
		}
		
	}
}
