package nxm.sys.lib;

import java.util.Hashtable;
import java.util.Map;

import nxm.sys.inc.DataTypes;
import nxm.sys.inc.InternalUseOnly;

/** Class containing the set of variables accessible within the current NeXtMidas shell */
public class Results extends Table {
  /** NOTE: this should be changed if the structure/fields of this class changes. */
  private static final long serialVersionUID = 2015110200370L; // updated in 3.7.0

  /** The flags required by Results. This was added in NeXtMidas 3.7.0 to replace override methods
   *  in this class with enhanced versions in Table. This replacement was done to ease maintainability
   *  even though it doesn't follow the pure object-oriented paradigm.
   *  @since NeXtMidas 3.7.0
   */
  private static final int RESULTS_FLAGS = IGNORE_EMPTY_INSERTS|INLINE_TYPE_CONVERSION|INLINE_RESULTS_CONVERSION;

  /** Initialize the results set. */
  @SuppressWarnings("LeakingThisInConstructor")
  public Results () {
    this.ref   = this;                // Setting ref=this matches pre-3.7.0 behavior
    this.flags = flags|RESULTS_FLAGS; // Need to force setting of RESULTS_FLAGS
  }

  /** Initialize the results set.
      @param ref The reference object to use.
   */
  public Results (Object ref) {
    this.ref   = ref;
    this.flags = flags|RESULTS_FLAGS; // Need to force setting of RESULTS_FLAGS
  }

  @Override
  public void setFlags (int mask) {
    super.setFlags(mask|RESULTS_FLAGS); // Need to force setting of RESULTS_FLAGS
  }

  /** Gets the length to end of the valid results name.
      @param buffer The String buffer containing the results name.
      @param index  The index into the buffer to begin reading from.
      @return The valid name length.
      @see #validNameLength(byte[],int,int)
   */
  @InternalUseOnly("Since NeXtMidas 3.7.0") // TODO: Should probably deprecate this and move to convert since not used here
  public static int validNameLength (String buffer, int index) {
    return validNameLength(buffer,null,index,buffer.length());
  }

  /** Gets the length to end of the valid results name.
      @param buffer The byte buffer containing the results name.
      @param index  The index into the buffer to begin reading from.
      @param size   The maximum size to check within (in the buffer).
      @return The valid name length.
      @see #validNameLength(String,int)
   */
  @InternalUseOnly("Since NeXtMidas 3.7.0: Only used by nxm.sys.lib.Convert") // TODO: Move to Convert and make private
  public static int validNameLength (byte[] buffer, int index, int size) {
    return validNameLength(null,buffer,index,size);
  }

  /** Method for determining the valid results name length.
   *  @param sb    The String containing the results name. (takes precedence over {@code bb})
   *  @param bb    The byte buffer containing the results name.
   *  @param index The index into the buffer to begin reading from.
   *  @param size  The maximum size to check within (in {@code sb}).
   *  @return The valid name length.
   */
  @InternalUseOnly("Since NeXtMidas 3.7.0: Only used by nxm.sys.lib.Results and nxm.sys.lib.Convert") // TODO: Move to Convert and make private
  public static int validNameLength (String sb, byte[] bb, int index, int size) {
    int i   = index; // current position
    int ip  = 0;     // nested parenthesis count (0=No parenthesis)
    int ips = index; // start of parenthesis
    int iq  = 0;     // index of quotes (0=no quotes, -N=End of quotes)
    int ic  = 0;     // index of colon
    int hd  = 0;     // has dot (0=no dots)
    char c;          // current character
    char cl = ' ';   // last character
    for (; i<size; i++,cl=c) {
      c = (sb!=null)? sb.charAt(i) : (char)bb[i];
           if (c>='A' && c<='Z') { }
      else if (c>='a' && c<='z') c-=32; // for later in-line type casting test
      else if (i==index) return 0;
      else if (c>='0' && c<='9') { }
      else if (c=='_') { }
      else if (c=='"' && ip>0) iq=1-iq;
      else if (iq>0) { } // inside quotes
      else if (cl=='.' && ip<=0) return i-1-index; // next character after dot MUST be alphanumeric
      else if (c=='.') { if (ip==0) { hd++; ic=0; }} // has dot, clear colon exception, if after
      else if (c=='(') { if (ip==0 && ic==0) ips=i; ip++; }
      else if (c==')' && ip>0) ip--;
      else if (c==':' && i-ips==1 && DataTypes.CONVERT_TYPE_LIST.indexOf(cl)>=0) { } // allow colon for in-line type casting
      else if (c==':' && ip==1) ic=i; // disallow colon if for file trimming
      else if (ip>0) { }
      else break;
    }
    if (cl=='.') i--;
    if (ic>0 && ips>0) i=ips;
    return i-index;
  }

  /** Old method for determining the valid results name length.
   *  @param sb    The String containing the results name. (takes precedence over {@code bb})
   *  @param bb    The byte buffer containing the results name.
   *  @param index The index into the buffer to begin reading from.
   *  @param size  The maximum size to check within (in {@code sb}).
   *  @return The valid name length.
   *  @deprecated Since added in NeXtMidas 3.7.0: Use {@link #validNameLength} in place of this.
   */
  @Deprecated
  public static int validNameLength_old (String sb, byte[] bb, int index, int size) {
    int ip=0; char c,cl=' ';
    for (int i=index; i<size; i++,cl=c) {
      c = (sb!=null)? sb.charAt(i) : (char)bb[i];
      if (c>='A' && c<='Z');
      else if (c>='a' && c<='z');
      else if (i==index) return 0;
      else if (c>='0' && c<='9');
      else if (c=='_');
      //jph  else if (cl=='.') return i-1-index; // next character after dot MUST be alphanumeric
      else if (cl=='.' && ip<=0) {
        return i-1-index; // next character after dot MUST be alphanumeric
      }
      else if (c=='.');
      else if (c=='(') ip++;
      else if (c==')' && ip>0) ip--;
      else if (c==':' && i-index==1); // in-line type casting
      else if (ip>0);
      else if (cl=='.') return i-1-index; // trailing dot
      else return i-index;
    }
    return size-index;
  }

  /** Adds a function to the current set of functions
      @param name function name/key
      @param f function to be added
   */
  @InternalUseOnly("Since NeXtMidas 3.7.0, this method was always meant for internal use only by Macro and XPANEL;XBC")
  @SuppressWarnings("UseOfObsoleteCollectionType") // Using Hashtable matches pre-3.7.0
  public void addFunction (String name, Function f) {
    if (fht==null) fht = new Hashtable<>(); // TODO: Change to other Map type?
    fht.put(name,f);
  }

  /** Marked internal and changed to return Map vs Hashtable since 3.7.0
      Returns the current set of functions
      @return mapping of functions
   */
  @InternalUseOnly("Since NeXtMidas 3.7.0, this method was always meant for internal use only by Macro and XPANEL;XBC")
  public Map<String,Function> getFunctions () { return fht; }

}
