package speiger.src.coreengine.utils.io.dataTag.parsing;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import speiger.src.coreengine.utils.io.dataTag.DataTag;
import speiger.src.coreengine.utils.io.dataTag.parsing.deserialize.IDataDeserializationContext;
import speiger.src.coreengine.utils.io.dataTag.parsing.deserialize.IDataDeserializer;
import speiger.src.coreengine.utils.io.dataTag.parsing.serialize.IDataSerializationContext;
import speiger.src.coreengine.utils.io.dataTag.parsing.serialize.IDataSerializer;
import speiger.src.coreengine.utils.io.dataTag.special.ListTag;

public class DataTagIO
{
	Map<Class<?>, IOEntry<?>> entries;
	SerializationContext context = new SerializationContext();
	
	DataTagIO(Map<Class<?>, IOEntry<?>> entries)
	{
		this.entries = entries;
	}

	public <T> T fromData(DataTag tag, Class<T> type) throws IOException
	{
		IOEntry<?> entry = entries.get(type);
		return entry == null || entry.deserializer == null ? null : type.cast(entry.deserializer.deserialize(tag, context));
	}
	
	public <T> List<T> fromData(DataTag tag, Class<T> type, List<T> list) throws IOException
	{
		IOEntry<?> entry = entries.get(type);
		if(entry != null && entry.deserializer != null)
		{
			if(tag instanceof ListTag)
			{
				for(DataTag subTag : tag.cast(ListTag.class))
				{
					list.add(type.cast(entry.deserializer.deserialize(subTag, context)));
				}
			}
			else
			{
				list.add(type.cast(entry.deserializer.deserialize(tag, context)));
			}
		}
		return list;
	}
	
	public DataTag toData(Object obj)
	{
		return toData(obj, obj.getClass());
	}
	
	@SuppressWarnings("unchecked")
	public DataTag toData(Object obj, Class<?> type)
	{
		IOEntry<Object> entry = (IOEntry<Object>)entries.get(type);
		return entry == null || entry.serializer == null ? null : entry.serializer.serialize(obj, context);
	}
	
	@SuppressWarnings("unchecked")
	public <K> DataTag toData(List<K> list, Class<K> type)
	{
		IOEntry<Object> entry = (IOEntry<Object>)entries.get(type);
		if(entry == null || entry.serializer == null)
		{
			return null;
		}
		ListTag tags = new ListTag();
		for(int i = 0;i<list.size();i++)
		{
			tags.add(entry.serializer.serialize(list.get(i), context));
		}
		return tags;
	}
	
	static class IOEntry<T>
	{
		IDataSerializer<T> serializer;
		IDataDeserializer<T> deserializer;
		
		public IOEntry(IDataSerializer<T> serializer, IDataDeserializer<T> deserializer)
		{
			this.serializer = serializer;
			this.deserializer = deserializer;
		}
		
	}
	
	class SerializationContext implements IDataDeserializationContext, IDataSerializationContext
	{
		@Override
		public DataTag serialize(Object obj)
		{
			return toData(obj);
		}

		@Override
		public DataTag serialize(Object obj, Class<?> clz)
		{
			return toData(obj, clz);
		}

		@Override
		public <T> T deserialize(DataTag tag, Class<T> type) throws IOException
		{
			return fromData(tag, type);
		}
		
	}
}
