package org.inventivetalent.nbt;

import com.google.gson.JsonObject;
import lombok.EqualsAndHashCode;
import org.inventivetalent.nbt.stream.NBTOutputStream;

import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

@EqualsAndHashCode(callSuper = true)
public class CompoundTag extends NBTTag<Map<String, NBTTag>> implements Iterable<Map.Entry<String, NBTTag>> {

	private final Map<String, NBTTag> value;

	public CompoundTag() {
		super("");
		this.value = new HashMap<>();
	}

	public CompoundTag(Map<String, NBTTag> value) {
		super("");
		this.value = new HashMap<>(value);
	}

	public CompoundTag(String name, Map<String, NBTTag> value) {
		super(name);
		this.value = new HashMap<>(value);
	}

	@Override
	public Map<String, NBTTag> getValue() {
		return value;
	}

	@Override
	public void setValue(Map<String, NBTTag> value) {
		this.value.putAll(value);
	}

	public NBTTag get(String name) {
		return value.get(name);
	}

	public void set(String name, NBTTag tag) {
		this.value.put(name, tag);
	}

	public void set(String name, byte b) {
		set(name, new ByteTag(name, b));
	}

	public void set(String name, short s) {
		set(name, new ShortTag(name, s));
	}

	public void set(String name, int i) {
		set(name, new IntTag(name, i));
	}

	public void set(String name, long l) {
		set(name, new LongTag(name, l));
	}

	public void set(String name, float f) {
		set(name, new FloatTag(name, f));
	}

	public void set(String name, double d) {
		set(name, new DoubleTag(name, d));
	}

	public void set(String name, String string) {
		set(name, new StringTag(name, string));
	}

	public void set(String name, byte[] b) {
		set(name, new ByteArrayTag(name, b));
	}

	public void set(String name, int[] i) {
		set(name, new IntArrayTag(name, i));
	}

	public void set(String name, boolean b) {
		set(name, (byte) (b ? 1 : 0));
	}

	@Override
	public JsonObject asJson() {
		JsonObject jsonObject = new JsonObject();
		for (Map.Entry<String, NBTTag> entry : value.entrySet()) {
			jsonObject.add(entry.getKey(), entry.getValue().asJson());
		}
		return jsonObject;
	}

	@Override
	public void write(NBTOutputStream nbtOut, DataOutputStream out) throws IOException {
		for (NBTTag tag : value.values()) {
			nbtOut.writeTag(tag);
		}
		out.writeByte(TagID.TAG_END);
	}

	@Override
	public int getTypeId() {
		return TagID.TAG_COMPOUND;
	}

	@Override
	public String getTypeName() {
		return "TAG_Compund";
	}

	@Override
	public Iterator<Map.Entry<String, NBTTag>> iterator() {
		return value.entrySet().iterator();
	}

	public String getNMSClass() {
		return "NBTTagCompound";
	}

	@Override
	public CompoundTag fromNMS(Object nms) throws ReflectiveOperationException {
		Class<?> clazz = NMS_CLASS_RESOLVER.resolve(getNMSClass());
		Class<?> nbtBaseClass = NMS_CLASS_RESOLVER.resolve("NBTBase");
		Field field = clazz.getDeclaredField("map");
		field.setAccessible(true);
		Map<String, Object> nmsMap = (Map<String, Object>) field.get(nms);
		CompoundTag compoundTag = new CompoundTag();
		for (Map.Entry<String, Object> nmsEntry : nmsMap.entrySet()) {
			byte typeId = (byte) nbtBaseClass.getMethod("getTypeId").invoke(nmsEntry.getValue());
			if (typeId == TagID.TAG_LIST) {
				compoundTag.set(nmsEntry.getKey(), ((NBTTag) NBTTag.forType(typeId).getConstructor(int.class, List.class).newInstance(0, new ArrayList<>())).fromNMS(nmsEntry.getValue()));
			} else {
				compoundTag.set(nmsEntry.getKey(), NBTTag.forType(typeId).newInstance().fromNMS(nmsEntry.getValue()));
			}
		}
		return compoundTag;
	}

	@Override
	public Object toNMS() throws ReflectiveOperationException {
		Class<?> clazz = NMS_CLASS_RESOLVER.resolve(getNMSClass());
		Field field = clazz.getDeclaredField("map");
		field.setAccessible(true);
		Object nms = clazz.newInstance();
		Map map = (Map) field.get(nms);
		for (Map.Entry<String, NBTTag> entry : this) {
			System.out.println(entry);
			map.put(entry.getKey(), entry.getValue().toNMS());
		}
		System.out.println(map);
		System.out.println("Set!");
		field.set(nms, map);
		return nms;
	}
}

@@ -265,9 +265,64 @@
 til.
-*
+HashMap;%0Aimport java.util.Iterator;%0Aimport java.util.Map
 ;%0A%0A@
@@ -3049,54 +3049,8 @@
 s);%0A
-%09%09CompoundTag compoundTag = new CompoundTag();
 %0A%09%09f
@@ -3235,36 +3235,24 @@
 LIST) %7B%0A%09%09%09%09
-compoundTag.
 set(nmsEntry
@@ -3266,112 +3266,26 @@
 (), 
-((NBTTag) NBTTag.forType(typeId).getConstructor(int.class, List.class).newInstance(0, new ArrayList%3C%3E())
+new ListTag(typeId
 ).fr
@@ -3333,20 +3333,8 @@
 %09%09%09%09
-compoundTag.
 set(
@@ -3442,19 +3442,12 @@
 urn 
-compoundTag
+this
 ;%0A%09%7D

update CompoundTag#fromNMS