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

import java.util.Arrays;
import java.util.UUID;

import speiger.src.coreengine.utils.io.dataTag.DataTag;
import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag;
import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag;
import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag;
import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag;
import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag;
import speiger.src.coreengine.utils.io.dataTag.simple.IntTag;
import speiger.src.coreengine.utils.io.dataTag.simple.LongTag;
import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag;
import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag;
import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag;
import speiger.src.coreengine.utils.io.dataTag.simple.StringTag;

public interface IMapTag extends DataTag
{
	public void putTag(String id, DataTag tag);
	public default void putList(String id, ListTag tag) { if(!tag.isEmpty()) putTag(id, tag); }
	public default void putMap(String id, MapTag tag) { if(!tag.isEmpty()) putTag(id, tag); }
	
	public default void putBoolean(String id, boolean value) {putByte(id, (byte)(value ? 1 : 0), (byte)0);}
	public default void putBoolean(String id, boolean value, boolean defaultValue) {putByte(id, (byte)(value ? 1 : 0), (byte)(defaultValue ? 1 : 0));}
	public default void putByte(String id, byte value) {putByte(id, value, (byte)0);}
	public default void putByte(String id, byte value, byte defaultValue) {
		if(value != defaultValue) putTag(id, new ByteTag(value));
	}
	
	public default void putShort(String id, short value) {putShort(id, value, (short)0);}
	public default void putShort(String id, short value, short defaultValue) {
		if(value != defaultValue) putTag(id, new ShortTag(value));
	}
	
	public default void putMedium(String id, int value) {putMedium(id, value, 0);}
	public default void putMedium(String id, int value, int defaultValue) {
		if(value != defaultValue) putTag(id, new MediumTag(value));
	}
	
	public default void putInt(String id, int value) {putInt(id, value, 0);}
	public default void putInt(String id, int value, int defaultValue) {
		if(value != defaultValue) putTag(id, new IntTag(value));
	}
	
	public default void putLong(String id, long value) {putLong(id, value, 0L);}
	public default void putLong(String id, long value, long defaultValue) {
		if(value != defaultValue) putTag(id, new LongTag(value));
	}
	
	public default void putFloat(String id, float value) {putFloat(id, value, 0F);}
	public default void putFloat(String id, float value, float defaultValue) {
		if(Float.floatToIntBits(value) == Float.floatToIntBits(defaultValue)) putTag(id, new FloatTag(value));
	}
	
	public default void putDouble(String id, double value) {putDouble(id, value, 0D);}
	public default void putDouble(String id, double value, double defaultValue) {
		if(Double.doubleToLongBits(value) == Double.doubleToLongBits(defaultValue)) putTag(id, new DoubleTag(value));
	}
	
	public default void putString(String id, String value) {putString(id, value, "");}
	public default void putString(String id, String value, String defaultValue) {
		if(!value.equals(defaultValue)) putTag(id, new StringTag(value));
	}
	
	public default void putByteArray(String id, byte[] data) {if(data.length > 0){putTag(id, new ByteArrayTag(data));}}
	public default void putByteArray(String id, byte[] data, byte[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new ByteArrayTag(data));}}
	public default void putShortArray(String id, short[] data) {if(data.length > 0){putTag(id, new ShortArrayTag(data));}}
	public default void putShortArray(String id, short[] data, short[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new ShortArrayTag(data));}}
	public default void putMediumArray(String id, int[] data) {if(data.length > 0){putTag(id, new MediumArrayTag(data));}}
	public default void putMediumArray(String id, int[] data, int[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new MediumArrayTag(data));}}
	public default void putIntArray(String id, int[] data) {if(data.length > 0){putTag(id, new IntArrayTag(data));}}
	public default void putIntArray(String id, int[] data, int[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new IntArrayTag(data));}}
	public default void putLongArray(String id, long[] data) {if(data.length > 0){putTag(id, new LongArrayTag(data));}}
	public default void putLongArray(String id, long[] data, long[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new LongArrayTag(data));}}
	public default void putFloatArray(String id, float[] data) {if(data.length > 0){putTag(id, new FloatArrayTag(data));}}
	public default void putFloatArray(String id, float[] data, float[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new FloatArrayTag(data));}}
	public default void putDoubleArray(String id, double[] data) {if(data.length > 0){putTag(id, new DoubleArrayTag(data));}}
	public default void putDoubleArray(String id, double[] data, double[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new DoubleArrayTag(data));}}
	public default void putStringArray(String id, String[] data) {if(data.length > 0){putTag(id, new StringArrayTag(data));}}
	public default void putStringArray(String id, String[] data, String[] defaultValue) {if(!Arrays.equals(data, defaultValue)){putTag(id, new StringArrayTag(data));}}
	
	public default void putUUID(String id, UUID data) { if(data != null) putLongArray(id, new long[]{data.getMostSignificantBits(), data.getLeastSignificantBits()});}
	
	public default <T extends Enum<T>> void putEnum(String id, T value)
	{
		int index = value == null ? 0 : value.ordinal()+1;
		if(index == 0) return;
		if(index < Byte.MAX_VALUE) putByte(id, (byte)index);
		else if(index < Short.MAX_VALUE) putShort(id, (short)index);
		else putInt(id, index);
	}
	
	public default <T extends Enum<T>> void putEnum(String id, T value, T defaultValue)
	{
		if(value == defaultValue || value == null) return;
		int index = value.ordinal()+1;
		if(index < Byte.MAX_VALUE) putByte(id, (byte)index);
		else if(index < Short.MAX_VALUE) putShort(id, (short)index);
		else putInt(id, index);
	}
	
	public DataTag get(String id);
	
	public default boolean getBoolean(String id) { return getByte(id, (byte)0) != 0;}
	public default boolean getBoolean(String id, boolean defaultValue) { return getByte(id, (byte)(defaultValue ? 1 : 0)) != 0;}
	public default byte getByte(String id) {return getByte(id, (byte)0);}
	public default byte getByte(String id, byte defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asByte() : defaultValue;
	}
	
	public default short getShort(String id) {return getShort(id, (short)0);}
	public default short getShort(String id, short defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asShort() : defaultValue;
	}
	
	public default int getMedium(String id) {return getMedium(id, 0);}
	public default int getMedium(String id, int defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue;
	}
	
	public default int getInt(String id) {return getInt(id, 0);}
	public default int getInt(String id, int defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue;
	}
	
	public default long getLong(String id) {return getLong(id, 0L);}
	public default long getLong(String id, long defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asLong() : defaultValue;
	}
	
	public default float getFloat(String id) {return getFloat(id, 0F);}
	public default float getFloat(String id, float defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asFloat() : defaultValue;
	}
	
	public default double getDouble(String id) {return getDouble(id, 0D);}
	public default double getDouble(String id, double defaultValue)
	{
		DataTag tag = get(id);
		return isPrimitve(tag) ? ((PrimitiveTag)tag).asDouble() : defaultValue;
	}
	
	public default String getString(String id) {return getString(id, "");}
	public default String getString(String id, String defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 8) ? ((StringTag)tag).get() : defaultValue;
	}
	
	public default byte[] getByteArray(String id) { return getByteArray(id, Defaults.EMPTY_BYTE); }
	public default byte[] getByteArray(String id, byte[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 9) ? ((ByteArrayTag)tag).get() : defaultValue;
	}
	
	public default short[] getShortArray(String id) { return getShortArray(id, Defaults.EMPTY_SHORT); }
	public default short[] getShortArray(String id, short[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 10) ? ((ShortArrayTag)tag).get() : defaultValue;
	}
	
	public default int[] getMediumArray(String id) { return getMediumArray(id, Defaults.EMPTY_INT); }
	public default int[] getMediumArray(String id, int[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 11) ? ((MediumArrayTag)tag).get() : defaultValue;
	}
	
	public default int[] getIntArray(String id) { return getIntArray(id, Defaults.EMPTY_INT); }
	public default int[] getIntArray(String id, int[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 12) ? ((IntArrayTag)tag).get() : defaultValue;
	}
	
	public default long[] getLongArray(String id) { return getLongArray(id, Defaults.EMPTY_LONG); }
	public default long[] getLongArray(String id, long[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 13) ? ((LongArrayTag)tag).get() : defaultValue;
	}
	
	public default float[] getFloatArray(String id) { return getFloatArray(id, Defaults.EMPTY_FLOAT); }
	public default float[] getFloatArray(String id, float[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 14) ? ((FloatArrayTag)tag).get() : defaultValue;
	}
	
	public default double[] getDoubleArray(String id) { return getDoubleArray(id, Defaults.EMPTY_DOUBLE); }
	public default double[] getDoubleArray(String id, double[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 15) ? ((DoubleArrayTag)tag).get() : defaultValue;
	}
	
	public default String[] getStringArray(String id) { return getStringArray(id, Defaults.EMPTY_STRING); }
	public default String[] getStringArray(String id, String[] defaultValue)
	{
		DataTag tag = get(id);
		return isType(tag, 16) ? ((StringArrayTag)tag).get() : defaultValue;
	}
	
	public default UUID getUUID(String id)
	{
		long[] data = getLongArray(id);
		return data == null || data.length != 2 ? null : new UUID(data[0], data[1]);
	}
	
	public default <T extends Enum<T>> T getEnum(String id, Class<T> clz)
	{
		int value = getInt(id);
		return value == 0 ? null : clz.getEnumConstants()[value-1];
	}
	
	public default <T extends Enum<T>> T getEnum(String id, Class<T> clz, T defaultValue)
	{
		int value = getInt(id);
		return value == 0 ? defaultValue : clz.getEnumConstants()[value-1];
	}
	
	public default MapTag getMap(String id)
	{
		DataTag tag = get(id);
		return isType(tag, 17) ? (MapTag)tag : new MapTag();
	}
	
	public default ListTag getList(String id)
	{
		DataTag tag = get(id);
		return isType(tag, 18) ? (ListTag)tag : new ListTag();
	}
	
	public default boolean isPrimitve(DataTag tag)
	{
		int id = tag == null ? 0 : tag.getId();
		return id >= 1 && id <= 7;
	}
	
	public default boolean isType(DataTag tag, int type)
	{
		return (tag == null ? 0 : tag.getId()) == type;
	}
	
	static class Defaults
	{
		private static final byte[] EMPTY_BYTE = new byte[0];
		private static final short[] EMPTY_SHORT = new short[0];
		private static final int[] EMPTY_INT = new int[0];
		private static final long[] EMPTY_LONG = new long[0];
		private static final float[] EMPTY_FLOAT = new float[0];
		private static final double[] EMPTY_DOUBLE = new double[0];
		private static final String[] EMPTY_STRING = new String[0];
	}
}