package org.spacehq.opennbt;

import org.spacehq.opennbt.tag.*;
import org.spacehq.opennbt.tag.custom.*;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * A registry containing different tag classes.
 */
public class TagRegistry {

	private static final Map<Integer, Class<? extends Tag>> idToTag = new HashMap<Integer, Class<? extends Tag>>();
	private static final Map<Class<? extends Tag>, Integer> tagToId = new HashMap<Class<? extends Tag>, Integer>();

	static {
		try {
			register(1, ByteTag.class);
			register(2, ShortTag.class);
			register(3, IntTag.class);
			register(4, LongTag.class);
			register(5, FloatTag.class);
			register(6, DoubleTag.class);
			register(7, ByteArrayTag.class);
			register(8, StringTag.class);
			register(9, ListTag.class);
			register(10, CompoundTag.class);
			register(11, IntArrayTag.class);

			register(60, DoubleArrayTag.class);
			register(61, FloatArrayTag.class);
			register(62, LongArrayTag.class);
			register(63, SerializableArrayTag.class);
			register(64, SerializableTag.class);
			register(65, ShortArrayTag.class);
			register(66, StringArrayTag.class);
		} catch(TagRegisterException e) {
			throw new RuntimeException("Failed to register default tags.", e);
		}
	}

	/**
	 * Registers a tag class.
	 *
	 * @param tag Tag class to register.
	 * @throws TagRegisterException If an error occurs while registering the tag.
	 */
	public static void register(int id, Class<? extends Tag> tag) throws TagRegisterException {
		if(idToTag.containsKey(id)) {
			throw new TagRegisterException("Tag ID \"" + id + "\" is already in use.");
		}

		if(tagToId.containsKey(tag)) {
			throw new TagRegisterException("Tag \"" + tag.getSimpleName() + "\" is already registered.");
		}

		idToTag.put(id, tag);
		tagToId.put(tag, id);
	}

	/**
	 * Gets the tag class with the given id.
	 *
	 * @param id Id of the tag.
	 * @return The tag class with the given id.
	 */
	public static Class<? extends Tag> getClassFor(int id) {
		return idToTag.get(id);
	}

	/**
	 * Gets the id of the given tag class.
	 *
	 * @param clazz The tag class to get the id of.
	 * @return The id of the given tag class, or -1 if it cannot be found.
	 */
	public static int getIdFor(Class<? extends Tag> clazz) {
		return tagToId.get(clazz);
	}

	/**
	 * Creates an instance of the tag with the given id, using the String constructor.
	 *
	 * @param id      Id of the tag.
	 * @param tagName Name to give the tag.
	 * @return The created tag, or null if it could not be created or the type does not exist.
	 */
	public static Tag createInstance(int id, String tagName) throws TagCreateException {
		Class<? extends Tag> clazz = idToTag.get(id);
		if(clazz == null) {
			throw new TagCreateException("Could not find tag with ID \"" + id + "\".");
		}

		try {
			Constructor<? extends Tag> constructor = clazz.getDeclaredConstructor(String.class);
			constructor.setAccessible(true);
			return constructor.newInstance(tagName);
		} catch(Exception e) {
			throw new TagCreateException("Failed to create instance of tag \"" + clazz.getSimpleName() + "\".", e);
		}
	}

}

@@ -1944,18 +1944,49 @@
 given id
+, or null if it cannot be found
 .%0A
-
 %09 */%0A%09pu
@@ -2036,24 +2036,78 @@
 r(int id) %7B%0A
+%09%09if(!idToTag.containsKey(id)) %7B%0A%09%09%09return null;%0A%09%09%7D%0A%0A
 %09%09return idT
@@ -2357,16 +2357,71 @@
 lazz) %7B%0A
+%09%09if(!tagToId.containsKey(clazz)) %7B%0A%09%09%09return -1;%0A%09%09%7D%0A%0A
 %09%09return
@@ -2644,71 +2644,82 @@
  tag
-, or null if it could not b
+.%0A%09 * @throws TagCreateException If an error occurs whil
 e creat
-ed or
+ing
  the t
-ype does not exist
+ag
 .%0A%09 

Correct behavior and javadocs in TagRegistry