package nxm.sys.lib;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.regex.Pattern;

import nxm.sys.inc.InternalUseOnly;

/** Represents a mask value created from an enumerated set of values. Use {@link #forType},
    {@link #with} or {@link #constant} to create a new instance.<br>
    @param <E> The enumerated type.

    @author  J. Czechowski IV
    @since   NeXtMidas 2.7.1
 */
@SuppressWarnings("unchecked")
public class MaskValue<E extends Enum<E>> implements Cloneable {
  private static final Pattern SEPARATOR = Pattern.compile("[,|]"); // used by utility setValue(..)

  protected final Class<E> type;    // The type.
  protected       boolean  isFinal; // Is the value "final" (this is only set in ...)
  protected       int      value;   // The mask value.
  private   final int      bitMask; // The bit mask.

  /** Creates a new instance with no bits set. */
  protected MaskValue (Class<E> type) {
    this.type    = type;
    this.bitMask = 0; // not used
  }

  /** Creates a new instance with the given bits set. */
  private MaskValue (Class<E> type, int len) {
    int bm = 0;
    for (int i = 0; i < len; i++) {
      bm |= 1<<i;
    }
    this.type    = type;
    this.bitMask = bm;
  }

  /** Creates an exact duplicate of this instance. A clone of an instance created by
      {@link #constant} shall its self be considered to have been created by {@link #constant}
      (i.e. can not be modified).
      @return An exact duplicate of this instance.
      @see #copy()
   */
  public MaskValue<E> clone () {
    try {
      return (MaskValue<E>)super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError("Could not clone instance of "+getClass());
    }
  }

  /** Creates a copy of this instance. Unlike {@link #clone()} a copy of an instance created by
      {@link #constant} can be modified.
      @return A copy of this instance.
      @see #clone()
   */
  public MaskValue<E> copy () {
    MaskValue<E> mv = clone();
    mv.isFinal = false;
    return mv;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Static Creation Methods
  //////////////////////////////////////////////////////////////////////////////////////////////////
  /** Gets a MaskValue appropriate for the given type. It is important to use this method since it
      will use the correct underlying implementation. The default (fastest) implementation is only
      good for enums with up to 32 entries. The second-best is good for enums with up to 64 entries
      and the third (slowest) implementation is good for an unlimited number of entries.
      @param type   The type.
      @param values Values to initially set (optional).
      @param <E>    The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> forType (Class<E> type, E ... values) {
    int len = type.getEnumConstants().length;

    MaskValue<E> mv;
         if (len <= 32) mv = new MaskValue<>(type,len);
    else if (len <= 64) mv = new MaskValueLong<>(type,len);
    else if (   true  ) mv = new MaskValueBitSet<>(type,len);

    for (E v : values) {
      mv.set(v);
    }
    return mv;
  }

  /** Gets a MaskValue that supports up to 64 flags that provides a level of backwards-compatibility
      for an existing 32-bit integer mask field.
      @param legacyObject The object holding the legacy field.
      @param legacyField  The legacy field (must be of type int).
      @param type         The type.
      @param values       Values to initially set (optional).
      @param <E>          The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> forLegacyField (Object legacyObject, String legacyField, Class<E> type, E ... values) {
    Field f;
    try {
      f = legacyObject.getClass().getField(legacyField);
    }
    catch (Exception e) {
      throw new IllegalArgumentException("Can not access field "+legacyField+" in "+legacyObject);
    }
    return forLegacyField(legacyObject, f, type, values);
  }

  /** Gets a MaskValue that supports up to 64 flags that provides a level of backwards-compatibility
      for an existing 32-bit integer mask field.
      @param legacyObject The object holding the legacy field.
      @param legacyField  The legacy field (must be of type int).
      @param type         The type.
      @param values       Values to initially set (optional).
      @param <E>          The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> forLegacyField (Object legacyObject, Field legacyField, Class<E> type, E ... values) {
    int len = type.getEnumConstants().length;

    MaskValue<E> mv;
         if (len <= 32) mv = new MaskValueLegacy32<>(legacyObject,legacyField,type,len);
    else if (len <= 64) mv = new MaskValueLegacy64<>(legacyObject,legacyField,type,len);
    else if (   true  ) throw new IllegalArgumentException("Legacy mask values only support 64 flags.");

    for (E v : values) {
      mv.set(v);
    }
    return mv;
  }

  /** Gets a MaskValue with the given values set. This is a short form of {@link #forType} where the
      type is automatically determined from the values given.
      @param values Values to initially set (must have at least 1 value).
      @param <E>    The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> with (E ... values) {
    // User should get a compile-time error if using varargs and values is empty when calling this method
    return forType((Class<E>)values[0].getClass(), values);
  }

  /** Creates a MaskValue with the given value set. This is identical to {@link #forType} except
      that once the MaskValue is created it is set to be "final" and any attempts to change it
      will result in an {@link UnsupportedOperationException} being thrown.<br>
      <br>
      Example:
      <pre>
        private static enum Fruit { KIWI, ORANGE, LEMON, BANANA };
        private static final MaskValue&lt;Fruit&gt; YELLOW_FRUIT = MaskValue.constant(Fruit.class, Fruit.BANANA, Fruit.LEMON);
      </pre>
      @param type   The type.
      @param values The values to set (optional).
      @param <E>    The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> constant (Class<E> type, E ... values) {
    MaskValue<E> mv = forType(type, values);
    mv.isFinal = true;
    return mv;
  }

  /** Creates a MaskValue with the given value set. This is identical to {@link #with} except
      that once the MaskValue is created it is set to be "final" and any attempts to change it
      will result in an {@link UnsupportedOperationException} being thrown.<br>
      <br>
      Example:
      <pre>
        private static enum Fruit { KIWI, ORANGE, LEMON, BANANA };
        private static final MaskValue&lt;Fruit&gt; YELLOW_FRUIT = MaskValue.constant(Fruit.BANANA, Fruit.LEMON);
      </pre>
      @param values The values to set (must have at least 1 value).
      @param <E>    The enum type
      @return A new mask value with the given flags set.
   */
  public static <E extends Enum<E>> MaskValue<E> constant (E ... values) {
    // User should get a compile-time error if using varargs and values is empty when calling this method
    MaskValue<E> mv = with(values);
    mv.isFinal = true;
    return mv;
  }

  /** Creates a new MaskValue from the given string.
      @param maskStr The mask string (see {@link #setValue(CharSequence,MaskValue)}).
      @param type    The underlying enumerated type (see {@link #forType}).
      @param <E>     The enum type
      @return The new MaskValue.
   */
  public static final <E extends Enum<E>> MaskValue<E> fromString (CharSequence maskStr, Class<E> type) {
    MaskValue<E> mv = MaskValue.forType(type);
    mv.setValue(maskStr, mv);
    return mv;
  }

  /** Creates a new MaskValue from the given string.
      @param maskStr The mask string (see {@link #setValue(CharSequence,MaskValue)}).
      @param current The "current" value (null = use default as current value).
      @param <E>     The enum type
      @param def     The default value (can not be null).
      @return The new MaskValue.
   */
  public static final <E extends Enum<E>> MaskValue<E> fromString (CharSequence maskStr, MaskValue<E> current, MaskValue<E> def) {
    MaskValue<E> mv = (current == null)? def.copy() : current.copy();
    mv.setValue(maskStr, def);
    return mv;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Instance methods
  //////////////////////////////////////////////////////////////////////////////////////////////////

  /** Checks the mask value for equality. Mask values for different enumerated types are never
      considered to be equal.
      @param obj The object to compare to this one.
      @return true if the object is a MaskValue of the same type and with the same flags set.
   */
  public synchronized boolean equals (Object obj) {
    if (!(obj instanceof MaskValue)) return false;
    MaskValue<?> mv = (MaskValue<?>)obj;
    return (mv.type == type) && (mv.value == value);
  }

  /** Produces a hash code for the mask value.
      @return A hash code for the mask value.
   */
  public synchronized int hashCode () {
    return value;
  }

  public synchronized String toString () {
    StringBuilder str = new StringBuilder("|");
    for (E flag : type.getEnumConstants()) {
      if (isSet(flag)) {
        str.append(flag).append('|');
      }
    }
    return str.toString();
  }

  /** Gets the value of a given flag.
      @param flag The flag to check.
      @return true if the flag is ON, false if it is OFF.
   */
  public synchronized boolean isSet (E flag) {
    return (value & (1 << flag.ordinal())) != 0;
  }

  /** Sets a given flag (this turns the option ON).
      @param flag The flag to set.
   */
  public synchronized void set (E flag) {
    if (isFinal) throw new ConstantValueException();
    value = value | (1 << flag.ordinal());
  }

  /** Un-sets a given flag (this turns the option OFF).
      @param flag The flag to un-set.
   */
  public synchronized void unset (E flag) {
    if (isFinal) throw new ConstantValueException();
    value = value & ~(1 << flag.ordinal());
  }

  /** Toggles a given flag (this turns the option OFF if it is ON and ON if it is OFF).
      @param flag The flag to toggle.
   */
  public synchronized void toggle (E flag) {
    if (isFinal) throw new ConstantValueException();
    value = value ^ (1 << flag.ordinal());
  }

  /** Gets the value of a given flag.
      @param mv The set of flags to check.
      @return true all flags set in <code>mv</code> are also set in this mask value, false otherwise.
   */
  public synchronized boolean isSet (MaskValue<E> mv) {
    return (value & mv.value) == mv.value;
  }

  /** Sets a set of flags (this turns the options ON).
      @param mv The set of flags to set.
   */
  public synchronized void set (MaskValue<E> mv) {
    if (isFinal) throw new ConstantValueException();
    value = value | mv.value;
  }

  /** Un-sets a set of flags (this turns the options OFF).
      @param mv The set of flags to un-set.
   */
  public synchronized void unset (MaskValue<E> mv) {
    if (isFinal) throw new ConstantValueException();
    value = value & ~mv.value;
  }

  /** Toggles a set of flags (this turns each of the options OFF if it is ON and ON if it is OFF).
      @param mv The set of flags to toggle.
   */
  public synchronized void toggle (MaskValue<E> mv) {
    if (isFinal) throw new ConstantValueException();
    value = value ^ mv.value;
  }

  /** Sets the this mask value to be equal to <code>mv</code>.
      @param mv The set of flags to turn ON/OFF.
   */
  public synchronized void setValue (MaskValue<E> mv) {
    if (isFinal) throw new ConstantValueException();
    value = mv.value;
  }

  /** Sets all of the flags (all flags ON). */
  public synchronized void setAll () {
    if (isFinal) throw new ConstantValueException();
    value = bitMask;
  }

  /** Un-sets all of the flags (all flags OFF). */
  public synchronized void setNone () {
    if (isFinal) throw new ConstantValueException();
    value = 0;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Utility methods.
  //////////////////////////////////////////////////////////////////////////////////////////////////
  /** Converts string to enum. */
  private final E fromString (String str) throws IllegalArgumentException {
    E[] enums = type.getEnumConstants();  // <-- Throws NPE if defsel is null
    ArrayList<E> validEnumValues = new ArrayList<>();

    // Try case-sensitive
    for (E e : enums) {
      if (e.toString().equals(str)) return e;
    } // end for loop

    // Try again case-insensitive
    for (E e : enums) {
      final String enumStr = e.toString();
      if (enumStr.equalsIgnoreCase(str)) return e;
      // BEGIN since 3.3.0: logic to hide enum constants with @InternalUseOnly annotation from valid values list
      boolean isInternalUseOnly = false;
      try {
        isInternalUseOnly = e.getClass().getField(enumStr).isAnnotationPresent(InternalUseOnly.class);
      } catch (NoSuchFieldException ex) {} // ignore any exceptions and add to valid values list
      if (!isInternalUseOnly) {
        validEnumValues.add(e);
      }
      // END since 3.3.0: logic to hide enum constants with @InternalUseOnly annotation from valid values list
    }

    throw new IllegalArgumentException("Mask value '"+str+"' not found in "+validEnumValues+"");
  }

  /** Sets the value of a given flag (ON/OFF).
      @param flag The flag to set.
      @param on   true to turn the flag ON, false to turn it OFF.
   */
  public final void setValue (E flag, boolean on) {
    if (on) set(flag);
    else    unset(flag);
  }

  /** Sets the value of a given flag (ON/OFF).
      @param flag The name of the flag to set.
      @param on   true to turn the flag ON, false to turn it OFF.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public final void setValue (String flag, boolean on) {
    setValue(fromString(flag), on);
  }

  /** Gets the value of a given flag. This is the same as calling <code>isSet(flag)</code>.
      @param flag The flag to check.
      @return true if the flag is ON, false if it is OFF.
   */
  public final boolean getValue (E flag) {
    return isSet(flag);
  }

  /** Gets the value of a given flag. This is the same as calling <code>isSet(flag)</code>.
      @param flag The name of the flag to check.
      @return true if the flag is ON, false if it is OFF.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public final boolean getValue (String flag) {
    return isSet(flag);
  }
  /** Gets the value of a given flag.
      @param flag The name of the flag to check.
      @return true if the flag is ON, false if it is OFF.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public final boolean isSet (String flag) {
    return isSet(fromString(flag));
  }

  /** Sets a given flag (this turns the option ON).
      @param flag The name of the flag to set.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public final void set (String flag) {
    set(fromString(flag));
  }

  /** Un-sets a given flag (this turns the option OFF).
      @param flag The name of the flag to un-set.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public final void unset (String flag) {
    unset(fromString(flag));
  }

  /** Toggles a given flag (this turns the option OFF if it is ON and ON if it is OFF).
      @param flag The name of the flag to toggle.
      @throws IllegalArgumentException if <code>flag</code> does not match one of the enumerated types.
   */
  public synchronized void toggle (String flag) {
    toggle(fromString(flag));
  }

  /** Sets the value of a given flags (ON/OFF).
      @param mv The set of flags to turn ON/OFF.
      @param on   true to turn the flag ON, false to turn it OFF.
   */
  public final void setValue (MaskValue<E> mv, boolean on) {
    if (on) set(mv);
    else    unset(mv);
  }

  /** Gets the value of a given flag. This is the same as calling <code>isSet(mv)</code>.
      @param mv The set of flags to check.
      @return true all flags set in <code>mv</code> are also set in this mask value, false otherwise.
   */
  public final boolean getValue (MaskValue<E> mv) {
    return isSet(mv);
  }
  /** Sets the value of a given flag (ON/OFF). The following is the official
      <a href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form">BNF</a> for the mask string
      entered:
      <pre>
        &lt;mask-string&gt;  ::=  &lt;keyword&lt;  |  &lt;keyword&lt; &lt;sep&gt; &lt;flags&gt;
                                             |  &lt;flags&gt;  |  &lt;hex-value&gt;
        &lt;flags&gt;        ::=  &lt;op-flag&gt;  |  &lt;op-flag&gt; &lt;sep&gt; &lt;flags&gt;
        &lt;op-flag&gt;      ::=  &lt;flag&gt;  |  &lt;operator&gt; &lt;flag&gt;
        &lt;sep&gt;          ::=  "|"  |  ","
        &lt;operator&gt;     ::=  "+"  |  "-"  |  "~"
        &lt;keyword&gt;      ::=  "DEFAULT"  |  "DEF"  |  "ALL"  |  "NONE"  |  ""
        &lt;flag&gt;         ::=  <i>One of the elements of the enumerated type</i>  |  ""
        &lt;hex-value&gt;    ::=  <i>DEPRECATED: Hex value for flag (see {@link #setHexValue(int)}).</i>
      </pre>
      The special keywords (not case-sensitive) do the following:
      <pre>
        "DEFAULT"   - Sets value to <code>def</code>
        "DEF"       - Sets value to <code>def</code> (alias for "DEFAULT")
        "ALL"       - Sets all flags (i.e. {@link #setAll()})
        "NONE"      - Un-sets all flags (i.e. {@link #setNone()})
        ""          - Un-sets all flags (alias for "NONE")
      </pre>
      The keyword must be the first (or only) thing in the mask string. If one of the enumerated
      entries matches a keyword, the keyword takes presidents but only for the first entry on the
      line. If users want to "protect" their flag settings, they can just surround them with bars
      (e.g. use "|DEF|GHI|" in place of "DEF|GHI").<br>
      <br>
      The operators do the following:
      <pre>
        "+"        - Sets the flag ({@link #set(String)})
        "-"        - Un-sets the flag ({@link #unset(String)})
        "~"        - Toggles the flag ({@link #toggle(String)})
      </pre>
      If an operator is given before the first flag (no &lt;keyword&gt;), the "starting point" is
      considered to be the current value of this MaskValue, and all changes are applied to it. If
      the first flag (no &lt;keyword&gt;) is given with no operator, it is taken to be a none
      {@link #setNone()} followed by a {@link #set(String)}. Any subsequent flags with no operator
      are taken to be a {@link #set(String)}.
      @param maskStr The mask string (null="DEFAULT").
      @param def     The default value.
   */
  public synchronized final void setValue (CharSequence maskStr, MaskValue<E> def) {
    if (maskStr == null) maskStr = "DEFAULT";

    // Check for <hex-value> (deprecated)
    if (StringUtil.isHexInteger(maskStr.toString())) {
      setHexValue(Convert.o2l(maskStr));
      Shell.deprecate("Use of HEX for mask values is deprected, use "+this+" in place of "+maskStr);
      return;
    }

    // Split on <separator>
    String[] tokens = SEPARATOR.split(maskStr);

    // Special case, no tokens
    if (tokens.length == 0) {
      setNone();
      return;
    }

    // Iterate through the tokens
    for (int i = 0; i < tokens.length; i++) {
      String token = tokens[i]; // <keyword> or <op-flag> or <flag>

      // Check for <keyword>
      if (i == 0) {
        if (token.equalsIgnoreCase("DEFAULT")) { setValue(def); continue; }
        if (token.equalsIgnoreCase("DEF"    )) { setValue(def); continue; }
        if (token.equalsIgnoreCase("ALL"    )) { setAll();      continue; }
        if (token.equalsIgnoreCase("NONE"   )) { setNone();     continue; }
        if (token.equalsIgnoreCase(""       )) { setNone();     continue; }
      }

      // Check for "" and ignore it ("||" in string).
      if (token.length() == 0) continue;

      // Check for <operator>
      char op = token.charAt(0);

      // Set to "NONE" if no <operator> on first one
      if ((i == 0) && (op != '+') && (op != '-') && (op != '~')) {
        setNone();
      }

      // Set the flag
      switch (op) {
        case '+':  set(token.substring(1));    break;
        case '-':  unset(token.substring(1));  break;
        case '~':  toggle(token.substring(1)); break;
        default:   set(token);                 break; // no operator given
      }
    }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Legacy Methods
  //////////////////////////////////////////////////////////////////////////////////////////////////

  /** Sets this mask value equal to the hex mask value. This is provided for
      backwards-compatibility with legacy mask values that were not type-safe.
      This does minimal error checking and is undefined w.r.t. high-order bits
      above the highest applicable bit for this type.
      @param val The hex mask value.
      @throws UnsupportedOperationException if E is a type with more than 32 elements.
   */
  public synchronized final void setHexValue (int val) {
    if (type.getEnumConstants().length > 32) {
      throw new UnsupportedOperationException("Can not get hex mask value for "+type);
    }
    value = val & bitMask;
  }

  /** Gets the hex mask value compatible with this mask value. This is provided for
      backwards-compatibility with legacy mask values that were not type-safe. The
      behavior of this method undefined w.r.t. high-order bits above the highest
      applicable bit for this type.
      @return The hex mask value.
      @throws UnsupportedOperationException if E is a type with more than 32 elements.
   */
  public synchronized final int getHexValue () {
    if (type.getEnumConstants().length > 32) {
      throw new UnsupportedOperationException("Can not get hex mask value for "+type);
    }
    return value;
  }

  /** Gets the hex mask value for the given enumerated value. This is provided for
      backwards-compatibility with legacy mask values that were not type-safe.
      @param value The value to get the hex mask value for.
      @param <E>   The enum type
      @return The hex mask value.
      @throws UnsupportedOperationException if the value is from a type with more than 32 elements.
   */
  public static <E extends Enum<E>> int getHexValue (E value) {
    Class<?> type = value.getClass();
    if (type.getEnumConstants().length > 32) {
      throw new UnsupportedOperationException("Can not get hex mask value for "+type);
    }
    return (1 << value.ordinal());
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Nested classes
  //////////////////////////////////////////////////////////////////////////////////////////////////
  /** Thrown if the user tries to set a MaskValue created with MaskValue.constant(..). */
  private static final class ConstantValueException extends UnsupportedOperationException {
    /** NOTE: this should be changed if the structure/fields of this class changes. */
    private static final long serialVersionUID = 2010061000290L;

    public ConstantValueException () {
      super("Can not modify a MaskValue created with MaskValue.constant(..)");
    }
  }

  /** An extension up to 64 bits. */
  private static class MaskValueLong<E extends Enum<E>> extends MaskValue<E> {
    protected       long val;
    private   final long bitMask;

    private MaskValueLong (Class<E> type, int len) {
      super(type);
      long bm = 0;
      for (int i = 0; i < len; i++) {
        bm |= 1L<<i;
      }
      this.bitMask = bm;
    }
    public synchronized boolean equals (Object obj) {
      if (!(obj instanceof MaskValueLong)) return false;
      MaskValueLong<?> mvl = (MaskValueLong<?>)obj;
      return (mvl.type == type) && (mvl.val == val);
    }
    public synchronized int hashCode () {
      return (int)(val >>> 32 ^ val);
    }
    public synchronized void setAll () {
      if (isFinal) throw new ConstantValueException();
      val = bitMask;
    }
    public synchronized void setNone () {
      if (isFinal) throw new ConstantValueException();
      val = 0L;
    }
    public synchronized boolean isSet (E flag) {
      return (val & (1L << flag.ordinal())) != 0L;
    }
    public synchronized void set (E flag) {
      if (isFinal) throw new ConstantValueException();
      val = val |  (1L << flag.ordinal());
    }
    public synchronized void unset (E flag) {
      if (isFinal) throw new ConstantValueException();
      val = val & ~(1L << flag.ordinal());
    }
    public synchronized void toggle (E flag) {
      if (isFinal) throw new ConstantValueException();
      val = val ^  (1L << flag.ordinal());
    }
    public synchronized boolean isSet (MaskValue<E> mv) {
      MaskValueLong<E>mvl = (MaskValueLong<E>)mv;
      return (val & mvl.val) == mvl.val;
    }
    public synchronized void set (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueLong<E>mvl = (MaskValueLong<E>)mv;
      val = val |  mvl.val;
    }
    public synchronized void unset (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueLong<E>mvl = (MaskValueLong<E>)mv;
      val = val & ~mvl.val;
    }
    public synchronized void toggle (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueLong<E>mvl = (MaskValueLong<E>)mv;
      val = val ^  mvl.val;
    }
    public synchronized void setValue (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueLong<E>mvl = (MaskValueLong<E>)mv;
      val = mvl.val;
    }
  }

  /** An extension beyond 64 bits. */
  private static final class MaskValueBitSet<E extends Enum<E>> extends MaskValue<E> {
    private final BitSet bs;
    private final int    len;

    private MaskValueBitSet (Class<E> type, int len) {
      super(type);
      this.bs  = new BitSet(len);
      this.len = len;
    }
    public synchronized boolean equals (Object obj) {
      if (!(obj instanceof MaskValueBitSet)) return false;
      MaskValueBitSet<?> mvbs = (MaskValueBitSet<?>)obj;
      return (mvbs.type == type) && bs.equals(mvbs.bs);
    }
    public synchronized int hashCode () {
      return bs.hashCode();
    }
    public synchronized void setAll () {
      if (isFinal) throw new ConstantValueException();
      bs.set(0, len, true);
    }
    public synchronized void setNone () {
      if (isFinal) throw new ConstantValueException();
      bs.clear();
    }
    public synchronized boolean isSet (E flag) {
      return bs.get(flag.ordinal());
    }
    public synchronized void set (E flag) {
      if (isFinal) throw new ConstantValueException();
      bs.set(flag.ordinal());
    }
    public synchronized void unset (E flag) {
      if (isFinal) throw new ConstantValueException();
      bs.clear(flag.ordinal());
    }
    public synchronized void toggle (E flag) {
      if (isFinal) throw new ConstantValueException();
      bs.flip(flag.ordinal());
    }
    public synchronized boolean isSet (MaskValue<E> mv) {
      MaskValueBitSet<E>mvbs = (MaskValueBitSet<E>)mv;
      BitSet temp = bs.get(0, bs.length());
      temp.and(mvbs.bs);
      return temp.equals(mvbs.bs);
    }
    public synchronized void set (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueBitSet<E>mvbs = (MaskValueBitSet<E>)mv;
      bs.or(mvbs.bs);
    }
    public synchronized void unset (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueBitSet<E>mvbs = (MaskValueBitSet<E>)mv;
      bs.andNot(mvbs.bs);
    }
    public synchronized void toggle (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      MaskValueBitSet<E>mvbs = (MaskValueBitSet<E>)mv;
      bs.xor(mvbs.bs);
    }
    public synchronized void setValue (MaskValue<E> mv) {
      if (isFinal) throw new ConstantValueException();
      setNone();
      set(mv);
    }
  }

  /** An extension up to 32 bits that provides a level of backwards-compatibility. */
  private static final class MaskValueLegacy32<E extends Enum<E>> extends MaskValue<E> {
    private final Object legacyObject;
    private final Field  legacyField;

    private MaskValueLegacy32 (Object legacyObject, Field legacyField, Class<E> type, int len) {
      super(type,len);
      if (legacyField.getType() != Integer.TYPE) {
        throw new IllegalArgumentException("Only int fields can be used.");
      }

      try {
        this.legacyObject = legacyObject;
        this.legacyField  = legacyField;
        this.value        = legacyField.getInt(legacyObject);
      }
      catch (Exception e) {
        throw new IllegalArgumentException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    private void getLegacy () {
      try {
        int legacyValue = legacyField.getInt(legacyObject);
        if (value != legacyValue) {
          Shell.warning("External changes to "+legacyObject+" made via "+legacyField+" this is deprecated.");
          value = legacyValue;
        }
      }
      catch (Exception e) {
        throw new RuntimeException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    private void setLegacy () {
      try {
        legacyField.setInt(legacyObject, value);
      }
      catch (Exception e) {
        throw new RuntimeException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    public synchronized boolean equals   (Object obj)      { getLegacy(); return super.equals(obj);  }
    public synchronized int     hashCode ()                { getLegacy(); return super.hashCode();   }
    public synchronized boolean isSet    (E flag)          { getLegacy(); return super.isSet(flag);  }
    public synchronized boolean isSet    (MaskValue<E> mv) { getLegacy(); return super.isSet(mv);    }

    public synchronized void    setAll   ()                {              super.setAll();     setLegacy(); }
    public synchronized void    setNone  ()                {              super.setNone();    setLegacy(); }
    public synchronized void    set      (E flag)          { getLegacy(); super.set(flag);    setLegacy(); }
    public synchronized void    unset    (E flag)          { getLegacy(); super.unset(flag);  setLegacy(); }
    public synchronized void    toggle   (E flag)          { getLegacy(); super.toggle(flag); setLegacy(); }
    public synchronized void    set      (MaskValue<E> mv) { getLegacy(); super.set(mv);      setLegacy(); }
    public synchronized void    unset    (MaskValue<E> mv) { getLegacy(); super.unset(mv);    setLegacy(); }
    public synchronized void    toggle   (MaskValue<E> mv) { getLegacy(); super.toggle(mv);   setLegacy(); }
    public synchronized void    setValue (MaskValue<E> mv) {              super.setValue(mv); setLegacy(); }
  }

  /** An extension up to 64 bits that provides a level of backwards-compatibility. */
  private static final class MaskValueLegacy64<E extends Enum<E>> extends MaskValueLong<E> {
    private final Object legacyObject;
    private final Field  legacyField;

    private MaskValueLegacy64 (Object legacyObject, Field legacyField, Class<E> type, int len) {
      super(type,len);
      if (legacyField.getType() != Integer.TYPE) {
        throw new IllegalArgumentException("Only int fields can be used.");
      }

      try {
        this.legacyObject = legacyObject;
        this.legacyField  = legacyField;
        this.val          = 0x00000000FFFFFFFFL & legacyField.getInt(legacyObject);
      }
      catch (Exception e) {
        throw new IllegalArgumentException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    private void getLegacy () {
      try {
        int legacyValue = legacyField.getInt(legacyObject);
        if (((int)val) != legacyValue) {
          Shell.warning("External changes to "+legacyObject+" made via "+legacyField+" this is deprecated.");
          val = (val & 0xFFFFFFFF00000000L) | (legacyValue & 0x00000000FFFFFFFFL);
        }
      }
      catch (Exception e) {
        throw new RuntimeException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    private void setLegacy () {
      try {
        legacyField.setInt(legacyObject, (int)val);
      }
      catch (Exception e) {
        throw new RuntimeException("Can not access "+legacyField+" in "+legacyObject, e);
      }
    }

    public synchronized boolean equals   (Object obj)      { getLegacy(); return super.equals(obj);  }
    public synchronized int     hashCode ()                { getLegacy(); return super.hashCode();   }
    public synchronized boolean isSet    (E flag)          { getLegacy(); return super.isSet(flag);  }
    public synchronized boolean isSet    (MaskValue<E> mv) { getLegacy(); return super.isSet(mv);    }

    public synchronized void    setAll   ()                {              super.setAll();     setLegacy(); }
    public synchronized void    setNone  ()                {              super.setNone();    setLegacy(); }
    public synchronized void    set      (E flag)          { getLegacy(); super.set(flag);    setLegacy(); }
    public synchronized void    unset    (E flag)          { getLegacy(); super.unset(flag);  setLegacy(); }
    public synchronized void    toggle   (E flag)          { getLegacy(); super.toggle(flag); setLegacy(); }
    public synchronized void    set      (MaskValue<E> mv) { getLegacy(); super.set(mv);      setLegacy(); }
    public synchronized void    unset    (MaskValue<E> mv) { getLegacy(); super.unset(mv);    setLegacy(); }
    public synchronized void    toggle   (MaskValue<E> mv) { getLegacy(); super.toggle(mv);   setLegacy(); }
    public synchronized void    setValue (MaskValue<E> mv) {              super.setValue(mv); setLegacy(); }
  }
}
