package com.sharkysoft.util;

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;

/**
 * Type-safe enumeration.
 * 
 * <p><b>Details:</b> An <code>EnumeratedType</code> defines a finite set of 
 * values for a property.</p>
 *
 * <p>In C, enumerate types are typically represented using the
 * <code>enum</code> construct.  This offers many of the benefits of
 * type-<wbr>safety without sacrificing the efficiency of using
 * <code>int</code>s to represent the values.  Unfortunately, Java has no such
 * construct, and it has become common and acceptable to directly represent
 * enumerated types using <code>final int</code>s.  Unfortunately, this
 * adaptation lacks type safety.</p>
 *
 * <p><code>EnumeratedType</code> facilitates the declaration of type-<wbr>safe
 * enumerated types.  Each element in the type's value set is represented using
 * both <code>int</code>s and typed objects.  Methods that accept or return
 * enumerated types can do so using type-<wbr>safe object values under most
 * circumstances, and convert them to <code>int</code>s when necessary, such as
 * in a <code>switch</code> statement.  Using enumerated types derived from this
 * base class not only encourages healthy code, but it also makes debugging
 * easier, since each type-<wbr>safe value can also be converted into a
 * meaningful string value using <code>toString</code>.</p>
 *
 * @author Sharky
 */
public abstract class EnumeratedType implements Comparable
{

	/**
	 * Magic int.
	 *
	 * <p><b>Details:</b> <code>mnValue</code> is the magic <code>int</code>
	 * constant associated with this value.</p>
	 */
	private final int mnValue;

	/**
	 * String form.
	 *
	 * <p><b>Details:</b> <code>msString</code> is the string representation this
	 * value, returned by <code>toString</code>.</p>
	 */
	private final String msString;

	/**
	 * Maps integer values to object values.
	 *
	 * <p><b>Details:</b> <code>gpValues</code> maps integer values to object values.
	 * This map is populated by the constructor and supports
	 * <code>toObject</code>.</p>
	 */
	private static final Map gpValues = new TreeMap();

	/**
	 * Initializes type-safe object value.
	 *
	 * <p><b>Details:</b> This constructor initializes type-safe object values.
	 * <code>inValue</code> is the magic <code>int</code> value associated with
	 * the enumerated value (usually declared as <code>public static final
	 * int</code> in the subclass).  <code>isString</code> is the string
	 * representation of the same value, which is usually just the
	 * <code>int</code> identifier in string form.</p>
	 *
	 * @param inValue int representation of value
	 * @param isString string representation of value
	 */
	protected EnumeratedType(final int inValue, final String isString)
	{
		mnValue = inValue;
		msString = isString;
		gpValues.put(new Integer(inValue), isString);
	}

	/**
	 * Converts object value to integer value.
	 *
	 * <p><b>Details:</b> <code>toInt</code> returns the unique "magic
	 * integer" associated with this type-safe value.  This is useful in
	 * <code>switch</code> statements and other situations where <code>int</code>
	 * represention of the value might be more efficient.</p>
	 *
	 * @return integer value
	 */
	public int toInt()
	{
		return mnValue;
	}

	/**
	 * Converts object value to string form.
	 *
	 * <p><b>Details:</b> <code>toString</code> converts this type-<wbr>safe
	 * object value to a string.  This may be useful for debugging.</p>
	 *
	 * @return string form
	 */
	public String toString()
	{
		return msString;
	}

	/**
	 * Converts int value to object value.
	 *
	 * <p><b>Details:</b> <code>toEnumeratedType</code> returns the type-<wbr>safe
	 * object value corresponding to the given <code>int</code> value.</p>
	 *
	 * <p>Because this is a generic implementation that returns an
	 * <code>EnumeratedType</code>, rather than the specific object type related
	 * defined by the subclass, it is a good idea to define a <code>static</code>
	 * helper method in the subclass that returns a more specifically typed
	 * value.</p>
	 *
	 * @param inValue integer value
	 * @return object value
	 */
	public static EnumeratedType toEnumeratedType(final int inValue)
	{
		final EnumeratedType vpValue = (EnumeratedType) gpValues.get(new Integer(inValue));
		if (vpValue == null)
			throw new NoSuchElementException("inValue=" + inValue);
		return vpValue;
	}

	/**
	 * Converts integer value to string form.
	 *
	 * <p><b>Details:</b> <code>toString</code> converts the given integer value
	 * to a string.  This may be useful for debugging.</p>
	 *
	 * @param inValue integer value
	 * @return string form
	 */
	public static String toString(final int inValue)
	{
		try
		{
			return toEnumeratedType(inValue).toString();
		}
		catch (NoSuchElementException ve)
		{
			return Integer.toString(inValue);
		}
	}

	/**
	 * Orders enumerated values.
	 *
	 * <p><b>Details:</b> <code>compareTo</code> satisfies the contract of
	 * <code>compareTo</code> in interface <code>java.lang.Comparable</code>.
	 * Enumerated values are ordered according to their magic integer.</p>
	 *
	 * <p>This implementation throws a <code>ClassCastException</code> if one
	 * object's class does not descend from the other object's class.</p>
	 *
	 * @param ipValue value to commpare
	 * @return order of values
	 */
	public int compareTo(final Object ipValue)
	{
		final Class vpThisClass = getClass();
		final Class vpThatClass = ipValue.getClass();
		if (!(vpThisClass.isAssignableFrom(vpThatClass) || vpThatClass.isAssignableFrom(vpThisClass)))
			throw new ClassCastException("this=" + vpThisClass.getName() + ", that=" + vpThatClass.getName());
		final int vnThatValue = ((EnumeratedType) ipValue).mnValue;
		if (mnValue < vnThatValue)
			return -1;
		if (mnValue > vnThatValue)
			return +1;
		return 0;
	}

}
