package net.lax1dude.eaglercraft.compat;

import java.io.Serializable;

public class CompatEnum<E extends CompatEnum<E>> {
	/**
	 * The name of this enum constant, as declared in the enum declaration. Most
	 * programmers should use the {@link #toString} method rather than accessing
	 * this field.
	 */
	private final String name;

	/**
	 * Returns the name of this enum constant, exactly as declared in its enum
	 * declaration.
	 *
	 * <b>Most programmers should use the {@link #toString} method in preference to
	 * this one, as the toString method may return a more user-friendly name.</b>
	 * This method is designed primarily for use in specialized situations where
	 * correctness depends on getting the exact name, which will not vary from
	 * release to release.
	 *
	 * @return the name of this enum constant
	 */
	public final String name() {
		return name;
	}

	/**
	 * The ordinal of this enumeration constant (its position in the enum
	 * declaration, where the initial constant is assigned an ordinal of zero).
	 *
	 * Most programmers will have no use for this field. It is designed for use by
	 * sophisticated enum-based data structures, such as {@link java.util.EnumSet}
	 * and {@link java.util.EnumMap}.
	 */
	private final int ordinal;

	/**
	 * Returns the ordinal of this enumeration constant (its position in its enum
	 * declaration, where the initial constant is assigned an ordinal of zero).
	 *
	 * Most programmers will have no use for this method. It is designed for use by
	 * sophisticated enum-based data structures, such as {@link java.util.EnumSet}
	 * and {@link java.util.EnumMap}.
	 *
	 * @return the ordinal of this enumeration constant
	 */
	public final int ordinal() {
		return ordinal;
	}

	/**
	 * Sole constructor. Programmers cannot invoke this constructor. It is for use
	 * by code emitted by the compiler in response to enum type declarations.
	 *
	 * @param name    - The name of this enum constant, which is the identifier used
	 *                to declare it.
	 * @param ordinal - The ordinal of this enumeration constant (its position in
	 *                the enum declaration, where the initial constant is assigned
	 *                an ordinal of zero).
	 */
	protected CompatEnum(String name, int ordinal) {
		this.name = name;
		this.ordinal = ordinal;
	}

	/**
	 * Returns the name of this enum constant, as contained in the declaration. This
	 * method may be overridden, though it typically isn't necessary or desirable.
	 * An enum type should override this method when a more "programmer-friendly"
	 * string form exists.
	 *
	 * @return the name of this enum constant
	 */
	public String toString() {
		return name;
	}

	/**
	 * Returns true if the specified object is equal to this enum constant.
	 *
	 * @param other the object to be compared for equality with this object.
	 * @return true if the specified object is equal to this enum constant.
	 */
	public final boolean equals(Object other) {
		return this == other;
	}

	/**
	 * Returns a hash code for this enum constant.
	 *
	 * @return a hash code for this enum constant.
	 */
	public final int hashCode() {
		return super.hashCode();
	}

	/**
	 * Throws CloneNotSupportedException. This guarantees that enums are never
	 * cloned, which is necessary to preserve their "singleton" status.
	 *
	 * @return (never returns)
	 */
	protected final Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	/**
	 * Returns the Class object corresponding to this enum constant's enum type. Two
	 * enum constants e1 and e2 are of the same enum type if and only if
	 * e1.getDeclaringClass() == e2.getDeclaringClass(). (The value returned by this
	 * method may differ from the one returned by the {@link Object#getClass} method
	 * for enum constants with constant-specific class bodies.)
	 *
	 * @return the Class object corresponding to this enum constant's enum type
	 */
	@SuppressWarnings("unchecked")
	public final Class<E> getDeclaringClass() {
		Class<?> clazz = getClass();
		Class<?> zuper = clazz.getSuperclass();
		return (zuper == CompatEnum.class) ? (Class<E>) clazz : (Class<E>) zuper;
	}

	/**
	 * Returns the enum constant of the specified enum type with the specified name.
	 * The name must match exactly an identifier used to declare an enum constant in
	 * this type. (Extraneous whitespace characters are not permitted.)
	 *
	 * <p>
	 * Note that for a particular enum type {@code T}, the implicitly declared
	 * {@code public static T valueOf(String)} method on that enum may be used
	 * instead of this method to map from a name to the corresponding enum constant.
	 * All the constants of an enum type can be obtained by calling the implicit
	 * {@code public static T[] values()} method of that type.
	 *
	 * @param <T>      The enum type whose constant is to be returned
	 * @param enumType the {@code Class} object of the enum type from which to
	 *                 return a constant
	 * @param name     the name of the constant to return
	 * @return the enum constant of the specified enum type with the specified name
	 * @throws IllegalArgumentException if the specified enum type has no constant
	 *                                  with the specified name, or the specified
	 *                                  class object does not represent an enum type
	 * @throws NullPointerException     if {@code enumType} or {@code name} is null
	 * @since 1.5
	 */
	public static <T extends CompatEnum<T>> T valueOf(Class<T> enumType, String name) {
		try {
			T result = (T) enumType.getField(name).get(null);
			if (result != null)
				return result;
			if (name == null)
				throw new NullPointerException("Name is null");
		} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ee) {
		}
		throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);
	}

}
