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

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

import com.google.gson.JsonObject;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
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)
	{
		List<Number> numbers = parseVertexData(obj, entries, excludeOptional);
		ByteBuffer buffer = ByteBuffer.allocate(vertexCount * VertexEntry.caculateByteSize(entries, excludeOptional)).order(ByteOrder.nativeOrder());
		for(int i = 0,offset=0,m=vertexCount*entries.size();i<m;i++)
		{
			VertexEntry entry = entries.get(i % entries.size());
			if(entry.isOptional() && excludeOptional) continue;
			entry.getType().getDataType().putIntoBuffer(buffer, numbers, offset, entry.getSize());
			offset+=entry.getSize();
		}
		return buffer;
	}
	
	public static List<Number> parseVertexData(JsonObject obj, List<VertexEntry> entries, boolean excludeOptional)
	{
		int stride = VertexEntry.caclulateStride(entries);
		int outStride = excludeOptional ? VertexEntry.calculateNonOptionalStride(entries) : stride;
		int[] vertexes = JsonUtil.parseIntArray(obj.getAsJsonArray("vertexes"));
		Number[] data = new Number[excludeOptional ? (vertexes.length / stride) * outStride : vertexes.length];
		for(int i = 0,inOff=0,outOff=0,m=entries.size();i<m;i++)
		{
			VertexEntry entry = entries.get(i);
			if(!entry.isOptional() || !excludeOptional)
			{
				deserializeIndecies(vertexes, JsonUtil.parseArray(obj.getAsJsonArray(entry.getName())), entry.getSize(), stride, inOff, data, outStride, outOff);
				outOff += entry.getSize();
			}
			inOff += entry.getSize();
		}
		return ObjectArrayList.wrap(data);
	}
	
	public static Object2ObjectMap<String, List<Number>> parseMappedVertexData(JsonObject obj, List<VertexEntry> entries, boolean excludeOptional)
	{
		Object2ObjectMap<String, List<Number>> mappedData = Object2ObjectMap.createLinkedMap();
		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.getName(), deserializeIndecies(vertexes, JsonUtil.parseArray(obj.getAsJsonArray(entry.getName())), entry.getSize(), stride, offset));
			offset += entry.getSize();
		}
		return mappedData;
	}
	
	public static <T> void deserializeIndecies(int[] indecies, T[] in, int size, int stride, int offset, T[] out, int outStride, int outOffset)
	{
		for(int i = 0,m=(indecies.length/stride)*size;i<m;i++)
		{
			out[outOffset + ((i/size) * outStride) + (i % size)] = in[indecies[offset + ((i/size) * stride) + (i % size)]];
		}
	}
	
	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;
	}
}
