package nxm.sys.intr;

import nxm.sys.lib.Command;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Dictionary;
import nxm.sys.lib.FileName;
import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Parser;
import nxm.sys.lib.StringUtil;
import nxm.sys.lib.Terminal;

/** Displays or changes availability of shell commands.

    @author Jeff Schoen / Jay Hallam / Neon Ngo
    @author J. Czechowski IV (support for %-macros)
*/
public class Menu extends Intrinsic {
  private static final int COLUMN_WIDTH = 26; // The default column width for use with more(..).

  /** function/action list */
  private static final String actList="LIST,PREFER,ENABLE,DISABLE";
  /** function/action constants for {@link #actList} */
  private static final int LIST=1, PREFER=2, ENABLE=3, DISABLE=4;

  private DataFile df;                  // Output file for /F
  private Data     db;                  // Data buffer for df
  private boolean  showOption = true;   // Show option for non-SYS options?
  private int      act;                 // The action/function to perform (see actList).
  private String   names;               // The names to list ("*" for all)
  private String   supports;            // The supports to list ("*" for all but "%")
  private String   options;             // The options to list

  /** default constructor */
  public Menu () {}

  @SuppressWarnings("deprecation") // for M.terminal - since 3.7.1 for NXM-2813
  public int process () {
    // Get parameters
    act        = MA.getSelectionIndex("FUNC", actList, LIST);
    names      = MA.getU("NAME");
    supports   = MA.getU("SUP");
    options    = MA.getU("OPT");
    showOption = MA.getState("/OPT",showOption);

    if ((MA.find("/F"))) {
      showOption = MA.getState("/OPT", false);  // default to false to output command names only (TODO: maybe change this behavior in future)
      FileName filename = MA.getFileName("/F", "menu.tmp", "menu.tmp");
      String format = MA.getFormat("/FORMAT", "XA", "#A"); // since 3.1.1: allow format override, but limit to ASCII types
      df = new DataFile(M, filename, "1000", format, 0);
      df.open(DataFile.OUTPUT);
      db = df.getDataBuffer(1);
      if (verbose) M.info(" ... writing to "+df.getURL());
    }
    else M.terminal.more(Terminal.INIT);

    // Translate "ALL" as alternate for "*"
    if (names.equals("ALL"))    names    = "*";
    if (supports.equals("ALL")) supports = "*";
    if (options.equals("ALL"))  options  = "*";

    for (int i=0; i<M.dictionary.getSize(); i++) {
      Parser command = new Parser ( M.dictionary.get(i) );
      String name = command.get(1);
      // At this point name looks like...COMMAND;OPT:n,
      int lname = name.indexOf(';');
      int lendName = name.indexOf(':'); if (lendName < 0) lendName = name.length();

      processCommand(name.substring(0,lname), name.substring(lname+1,lendName), command.get(2));
    }

    // BUG 2281 - only find percent macros if support is "*" (ALL) or "%" (user macros)
    if (supports.equals("*") || StringUtil.containsCommonCharacter(supports,"%")) {
      String[] percentMacros = Dictionary.findPercentMacros(M);
      for (String percentMacro : percentMacros) {
        processCommand(percentMacro, null, "%");
      }
    }

    if (df !=null) df.close();
    else M.terminal.more(Terminal.EXIT);

    return FINISH;
  }

  /** Takes care of a single command. Only called from process(..). */
  @SuppressWarnings("deprecation")  // for M.terminal - since 3.7.1 for NXM-2813
  private void processCommand (String name, String option, String support) {
    int width = COLUMN_WIDTH; // Width for use with M.terminal.more(..)
    if (!supports.equals("*") && !StringUtil.containsCommonCharacter(supports,support)) return; // Switched to use moved method in STringUtil - since 3.9.2 for jdk modularity
    if (!options.equals("*")  && (option!=null) && !options.equals(option)) return;
    if (!matchName(names+"*",name)) return;

    switch (act) {
      case LIST:
        boolean showOpt = showOption && (option != null) && !option.equalsIgnoreCase("SYS");
        String cmdName  = (showOpt)? name+";"+option : name;
        if (df !=null) {
          db.fromString(cmdName);
          df.write(db);
        } else {
          M.more(" "+cmdName+"-"+support,width);   // since 3.7.1 using M.more instead of M.terminal.more - NXM-1813
        }
        break;
      case PREFER:  M.warning("MENU: Support for FUNC=PREFER not implemented.");  break;
      case ENABLE:  M.warning("MENU: Support for FUNC=ENABLE not implemented.");  break;
      case DISABLE: M.warning("MENU: Support for FUNC=DISABLE not implemented."); break;
    }
  }

  /** Checks to see if a command name matches a given pattern. */
  private static boolean matchName (String pattern, String name) {
    if (!pattern.startsWith("%") && name.startsWith("%")) {
      name = name.substring(1); // Omit the "%" from the comparison
    }

    return StringUtil.isWildcardMatch(pattern, name);
  }

  /** Gets a list of commands from the command dictionary. <br>
      <br>
      <code>"*"</code>, <code>"ALL"</code> or <code>null</code> may be passed
      in for <code>names</code>, <code>supports</code>, or <code>options</code>
      to indicate that no filtering should be done based on that charactersitic.
      <br>
      <br>
      Additionally <code>names</code> may use the <code>'*'</code> and
      <code>'?'</code> wildcards.

      @param midas       The midas context to use.
      @param names       The names of the commands.
      @param supports    The command support (must be an the allowable support
                         identifier: <code>'P'</code>, <code>'H'</code>, etc.).
      @param options     The option tree.
      @param showOption  Indicates if the option should be shown for options NOT
                         in the <code>SYS</code> tree.
      @param showSupport Indicates if the command's support should be shown.

      @return A list of the commands from the command dictionary that match the
              specified list of commands.
   */
  public static String[] getCommands (Midas midas, String names, String supports,
                                      String options, boolean showOption, boolean showSupport) {
    return Command.getMenuCommands(midas, names, supports, options, showOption, showSupport); // actual logic moved in 3.9.2 for NXM-3341 jdk modularity
  }
}
