package nxm.sys.lib;

/**
  Updates the commands.cnf file for a specified option tree.

  The input arguments to this class are:<br>
  NAME=value SUPPORT=value OPT=value NARGS=num DEF1=defstring DEF2=defstring...

  @author Teri Vokus
  @author Jay Hallam
*/
public class CommandLine extends Object {

  private String RawCommandName     = null; // Raw name includes * if applicable
  private String CommandOption      = null;
  private String CommandSupport     = null;
  private String CommandNargsString = null;
  private String CommandDefaults    = null;

  /** The default option tree (<code>{@value}</code>). */
  public static final String DEFAULT_OPTION  = "USER";

  /** The default support to assume (<code>{@value}</code>). */
  public static final String DEFAULT_SUPPORT = "M";

  /** Valid support characters (<code>{@value}</code>).
      <pre>
        I = Intrinsic
        P = Primitive (Java)
        H = Host primitive (Fortran or C)
        M = Macro
        D = Deprecated
        O = Operating System
        U = Operating System
      </pre>
  */
  public static final String VALID_SUPPORTS = "IPMHDOU";

  /** All valid support characters (<code>{@value}</code>). This includes everything in
      {@link #VALID_SUPPORTS} plus <code>"%"</code> used to identify user-macros.
  */
  public static final String ALL_SUPPORTS = VALID_SUPPORTS+"%";

  private static boolean Debug = false;
  // Constructors

  /** Constructor to search commands files for command
   *
   *  @param cmdName command name
   *  @param path    path
   */
  public CommandLine (String cmdName, String path) {
    setAllFromFile (cmdName, path);
  }

  /** Constructor given a text command line from the commands file
   *  @param cmdLine command line
   */
  public CommandLine (String cmdLine) {
    this(parseRawName(cmdLine),
         null,
         parseSupport(cmdLine),
         parseNargsString(cmdLine),
         parseDefaults(cmdLine));
  }

  /** Constructor to build a command line from the parts (int nargs)
   *
   *  @param cmdName  command name
   *  @param opt      option tree name
   *  @param support  'M' macro 'P' primitive 'I' intrinsic
   *  @param nargs    number of arguments (as int)
   *  @param defaults defaults
   */
  public CommandLine (String cmdName, String opt, String support, int nargs, String defaults) {
    this(cmdName, opt, support, Integer.toString(nargs), defaults);
  }

  /** Constructor to build a command line from the parts (string nargs)
   *
   *  @param cmdName  command name
   *  @param opt      option tree name
   *  @param support  'M' macro 'P' primitive 'I' intrinsic
   *  @param sargs    number of arguments (as String)
   *  @param defaults defaults
   */
  public CommandLine (String cmdName, String opt, String support, String sargs, String defaults) {
    setName(cmdName);
    setOption(opt);
    setSupport(support);
    setNargsString(sargs);
    setDefaults(defaults);
  }

//*********************************************************************//
//                         GETS - PUBLIC                               //
//*********************************************************************//

  /** Get the command name without the abbreviation marker
   @return the command name without the abbreviation marker.
   */
  public String getName () {
    String cmdName = RawCommandName;
    if (cmdName != null) {
      int lstar = cmdName.indexOf("*");
      if ( lstar > 0 ) {
        cmdName = cmdName.substring(0,lstar)+cmdName.substring(lstar+1);
      }
    }
    return cmdName;
  }

  /** Get the abbreviated command name
      @return the abbreviated command name.
   */
  public String getAbbrevName () {
    String cmdName = RawCommandName;
    int lstar = cmdName.indexOf("*");
    if ( lstar > 0 ) {
       cmdName = cmdName.substring(0,lstar);
    }
    return cmdName;
  }

  /** Get the RAW command name with abbreviation marker
      @return the raw command name with the abbreviation marker.
   */
  public String getRawName () {
    return RawCommandName;
  }

  /** Get the support name
      @return the support name.
   */
  public String getSupport () {
   return CommandSupport;
  }

  /** Get the number of arguments string
      @return the number of arguments as a string.
   */
  public String getNargsString () {
    return CommandNargsString;
  }

  /** Get the number of arguments integer
      @return the number of arguments as an integer.
   */
  public int getNargs () {
    return nargsStringToInt (CommandNargsString);
  }

  /** Get the defaults string
      @return the command defaults as a string.
   */
  public String getDefaults () {
    return CommandDefaults;
  }

  /** Get a specific default
      @param defnum the index of the command
      @return the default command at index [defnum]
   */
  public String getDefault (int defnum) {
    String defstr = null;
    if (CommandDefaults != null) {
      Parser defParser = new Parser (CommandDefaults);
      defParser.clean();
      defstr = defParser.get(defnum);
    }
    return defstr;
  }

  /** Get the command line
      @return the command line string.
   */
  public String getCommandLine () {
    String cmdLine = "";
    if (RawCommandName != null) {
      cmdLine = buildCommandLine (RawCommandName, CommandSupport,
                                  CommandNargsString, CommandDefaults);
    }
    return cmdLine;
  }

  /** Get the option name
      @return the option in use.
   */
  public String getOption () {
    return CommandOption;
  }

  /** Was the command found in the a commands file?
      @param path the path to the commands file.
      @return true if the command was found, false otherwise.
   */
  public boolean isInCommandsFile (String path)  {
    boolean exists = false;
    if (getCommandLine (getName(), path) != null) { exists = true; }
    return(exists);
  }

  /** Convert a number of arguments string into an integer
      @param sargs the number of arguments as a String.
      @return the number of arguments as an integer.
   */
  public static int nargsStringToInt (String sargs) {
    int nargs = -1;
    // change the string number into an int
    if (sargs != null) {
      try {
        // check for the '+' after the number
        int index = sargs.indexOf("+");
        if (index > 0) {
          sargs = sargs.substring(0,index);
        }

        // extract the number from the input string
        nargs = Integer.parseInt(sargs);
      }
      catch (NumberFormatException e) {
        //debug("nargsStringToInt: number format exception "+e);
        nargs = 0;
      }
    }  // sargs != null
    return nargs;
  }
//*********************************************************************//
//                	Command line parsing 	                       //
//*********************************************************************//
  /** Extract the support from the command line
      @param cmdLine the command line string.
      @return the support.
   */
  public static String parseSupport (String cmdLine) {
    Parser cmdParser = new Parser (cmdLine);
    cmdParser.clean();
    return cmdParser.get(2);
  }

  /** Extract the command name (with first * removed) from the command line
      @param cmdLine the command line string.
      @return the command name (with first * removed).
   */
  public static String parseName (String cmdLine) {
    String name = parseRawName (cmdLine);
    int la = name.indexOf("*");
    if ( la > 0 ) {
      name = name.substring(0,la)+name.substring(la+1);
    }
    return name;
  }

  /** Extract the command name from the command line (includes any *'s)
      @param cmdLine the command line string.
      @return the command name.
   */
  public static String parseRawName (String cmdLine) {
    Parser cmdParser = new Parser (cmdLine);
    cmdParser.clean();
    return cmdParser.get(1);
  }

  /** Extract the number of args from the command line
      @param cmdLine the command line string.
      @return the number of arguments as a String.
   */
  public static String parseNargsString (String cmdLine) {
    // Get the number of arguments as a string
    Parser cmdParser = new Parser (cmdLine);
    cmdParser.clean();
    return cmdParser.get(3);
  }

  /** Parses the command string for defaults
      @param cmdLine the command line string.
      @return comma-separated list of defaults.
   */
  public static String parseDefaults (String cmdLine) {
    // Parse the command string for defaults
    Parser cmdParser = new Parser (cmdLine);
    cmdParser.clean();
    int nargs = getNargs (cmdLine);
    String defaultsString;
    String val;
    if (nargs > 0) {
      val = cmdParser.get(4);
      if (val==null || val=="" || val==" ") val =",";   // BUG 1496
      defaultsString = val+",";
      for (int i=2; i<= (cmdParser.elements() - 3); i++){
        val =  cmdParser.get(i+3);
        if (val==null || val=="" || val==" ") val =","; // BUG 1496
        defaultsString = defaultsString+val+",";
      }
    }
    else {
      defaultsString = "";
    }
    debug ("parseDefaults; returning "+defaultsString);
    return defaultsString;
  }

//*********************************************************************//
//                             SETS                                    //
//*********************************************************************//

  /** Set the raw command command name
      @param name the name to be set.
   */
  public void setName (String name)  {
    if (name != null) RawCommandName = name.toUpperCase();
  }

  /** Set abbreviated command name.
      If the raw name exists: <br>
        (1) This inserts the abbrevation marker <br>
     or (2) if the abbreviation name contains the marker the name is set <br>
     If the raw name does NOT exist this sets the raw name.

     @param abbrevName the name to be set.
     @return true if the abbreviated command name was successfully set.
  */
  public boolean setAbbrevName (String abbrevName)  {
    String setNameStr = null;
    String cmdName = getName();
    String rawCmdName = getRawName();

    if (abbrevName.equalsIgnoreCase(rawCmdName)) {
      // Abbreviation is same as name, no action necessary
      setNameStr = rawCmdName+"*";
    } else if (abbrevName.length() == cmdName.length()) {
      // DR 1117: Make sure a * is inserted so abbreviation is explicit.
      setNameStr = abbrevName+"*"; // Abbrev = 'mycommand' and the command is 'mycommand'
    } else {
      int lstar = abbrevName.indexOf("*");
      if ( lstar > 0 ) {
        // Input of the form mycomm*and
        String nameToStar = abbrevName.substring(0,lstar);
        if (cmdName.length() >= lstar &&
            cmdName.substring(0,lstar).equalsIgnoreCase(nameToStar)) {
          String rawName = cmdName.substring(0,lstar);
          String afterStar = cmdName.substring(lstar);
          if (afterStar.length() > 0) rawName = rawName + "*" + afterStar;
          setNameStr = rawName;
        }
      } else {
        // Input of the form mycomm, where the command
        // is mycommand
        int la = abbrevName.length();
        if ( la > 0 && cmdName.length() >= la) {
          String cmdAbbrev = cmdName.substring(0,la);
          if (cmdAbbrev.equalsIgnoreCase(abbrevName)) {
            // The abbreviation may be the same as the command
            // in which case no * is necessary.
            String rawName = cmdName.substring(0,la);
            if (!cmdAbbrev.equalsIgnoreCase(cmdName)) {
              rawName = rawName+"*"+cmdName.substring(la);
            }
            setNameStr = rawName;
          }
        }
      }
    }
    if (setNameStr != null) setName(setNameStr);
    return (setNameStr != null);
  }

  /** Set option tree name
   *  @param opt the option tree to be set.
   */
  public void setOption (String opt)  {
    CommandOption = opt;
  }
  /** Set command support
      @param support the support to be set.
   */
  public void setSupport (String support)  {
    CommandSupport = support;
  }

  /** Set command number of arguments string with a string
      @param nargsString the number of arguments to be set.
   */
  public void setNargsString (String nargsString)  {
    // Use the wrapper class to verify that the string
    // is OK to be used as an int.
    int nargs = 0;
    String sargs = nargsString;
    int plusIndex = nargsString.indexOf("+");
    Integer I;
    String moreString = "";
    // A plus (=) is allowed as well
    if (sargs != null) {
      if (plusIndex > 0) {
        sargs = sargs.substring(0,plusIndex);
        moreString = "+";
      }
      try {
        nargs = Integer.parseInt(sargs);
      } catch ( NumberFormatException e ) {
        nargs = 0;
      }

    }
    setNargsString (nargs);
    CommandNargsString = CommandNargsString + moreString;
  }

  /** Set command defaults
      @param defaults the command defaults to be set.
   */
  public void setDefaults (String defaults) {
    CommandDefaults = defaults;
  }

  /** Check to make sure a support string is a valid support.
      Note, the space character is NOT a valid command support character.
      @param in command support (this can have multiple characters)
      @return returns false if <code>in</code> is null, blank,
              or contains an invalid command support character (i.e. not in set
              of {@link #VALID_SUPPORTS}.)


   */
  public static boolean isValidSupport (String in) {
    boolean status;

    if (in == null) {
      status = false;                               // false if null
    }
    else {
      char ch;
      int len = in.length();
      status = (len > 0);                           // false if empty string
      for (int ii=0; ii<len && status; ii++) {      // break out if status is false
        ch = in.charAt(ii);
        status = (VALID_SUPPORTS.indexOf(ch) >= 0); // false if ch is not in VALID_SUPPORTS
      }
    }

    return status;
  }

  /** Get the valid support characters
      @return a string of the valid support characters.
   */
  public static String getValidSupports () {
    return VALID_SUPPORTS;
  }

  /** Build the command line explicitly without looking into text file
      @param cmdName      the command name.
      @param support      the command support.
      @param nargsString  the number of arguments.
      @param defaults     the command defaults
      @return             a command line string.
   */
  public static String buildCommandLine (String cmdName, String support, String nargsString, String defaults) {

    String line    = "";
    String tabstr  = "";

    // Places tab(s) or space between name and support depending on the command name length
    String cmdNamePad     = StringUtil.padRight(cmdName, 15)+" ";
    String padString = StringUtil.padRight(support + ","+nargsString, 7)+" ";

    // Build the command line
    line  = cmdNamePad + padString + defaults;

    return line;
  }

//*********************************************************************//
//                          PRIVATE UTILITIES                          //
//*********************************************************************//
  /* Get the option name */
  private String getOption (String path) {
    return getOption (RawCommandName, path);
  }

  /* Get the number of arguments integer from a command line */
  private static int getNargs (String cmdLine) {
    int nargs;
    String sargs = parseNargsString (cmdLine);
    // change the string number into an int
    nargs = nargsStringToInt (sargs);
    return nargs;
  }

  /* Set command number of arguments string with an integer */
  private void setNargsString (int nargs) {
    CommandNargsString = Integer.toString(nargs);
  }

  /* Get option from name and path */
  private static String getOption (String cmdName, String path) {
    String optionOut = null;
    String cmdLine = null;
    Parser pathParser = new Parser (path);

    // Loop through all options until command name is
    // found in the file.  Return null otherwise
    for (int i=1; i<=pathParser.elements(); i++) {
      String opt = pathParser.get(i);
      Midas M = new Midas ();
      String fname = M.io.name ("CFG","COMMANDS","CNF",opt);
      TextFile textFile = new TextFile (M, fname);
      cmdLine = getCommandLineFromText (cmdName, textFile);
      if (cmdLine != null) {
        optionOut = opt;
        break;
      }
    }
    return optionOut;
  }

  /* Get command line from name and path */
  private static String getCommandLine (String cmdName, String path) {
    String cmdLineOut = null;
    Parser pathParser = new Parser (path);

    // Loop through all options until command name is
    // found in the file.  Return null otherwise
    for (int i=1; i<=pathParser.elements(); i++) {
      String opt = pathParser.get(i);
      Midas M = new Midas ();
      String fname = M.io.name ("CFG","COMMANDS","CNF",opt);
      TextFile textFile = new TextFile (M, fname);

      String cmdLine = getCommandLineFromText (cmdName, textFile);
      if (cmdLine != null) {
        cmdLineOut = cmdLine;
        break;
      }
    }
    return cmdLineOut;
  }

  /* Set all class variables */
  private void setAllFromFile (String cmdName, String path) {
    Parser pathParser = new Parser (path);

    // Loop through all options until command name is
    // found in the file and then set remaining class variables
    String opt = null;
    Midas M = new Midas ();
    String fname = null;
    TextFile textFile;
    String cmdLine = null;
    for (int i=1; i<=pathParser.elements(); i++) {
      opt = pathParser.get(i);
      fname = M.io.name ("CFG","COMMANDS","CNF",opt);
      textFile = new TextFile (M, fname);
      cmdLine = getCommandLineFromText (cmdName, textFile);
      if (cmdLine != null) {
        setOption      (opt);
        // Set the name
        setName(parseRawName (cmdLine) );
        setSupport(parseSupport (cmdLine) );
        setNargsString(parseNargsString (cmdLine) );
        setDefaults(parseDefaults (cmdLine) );
        break;
      }
    }
    return;
  }

  /* Get the command line from the commands file. */
  private static String getCommandLineFromText (String cmdName, TextFile infile) {
    int la;
    boolean done = false;
    String lineout = null;

    if (cmdName != null) {
      if (infile.open(TextFile.INPUT|TextFile.NOABORT)) {
        while (!done) {
          String line = infile.read();
          // If there is a problem reading the input file - stop processing
          if (line == null) {
            done = true;
	  }
          else {
            String curname = parseRawName (line);
            la = curname.indexOf("*");
            if (la>0) {
              curname = curname.substring(0,la)+curname.substring(la+1);
            }
            if (curname.compareTo(cmdName) == 0) {
              lineout = line;
              done = true;
            }
          }
        }
        infile.close();
      }
    }
    return lineout;
  }  // End of getCommandLine method

  /* Debug writes */
  private static void debug (String str) {
    if (Debug) System.out.println (str);
  }

} // End of class
