package nxm.sys.test.junit.lib;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import nxm.sys.inc.ListFile;
import nxm.sys.inc.PlotFile;
import nxm.sys.lib.Args;
import nxm.sys.lib.BaseFile;
import nxm.sys.lib.CsvFile;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.DbfFile;
import nxm.sys.lib.ImageFile;
import nxm.sys.lib.Keywords;
import nxm.sys.lib.MaskValue;
import nxm.sys.lib.Message;
import nxm.sys.lib.MessageQueue;
import nxm.sys.lib.Midas;
import nxm.sys.lib.ShapeFile;
import nxm.sys.lib.Shell;
import nxm.sys.lib.TextFile;
import nxm.sys.lib.Util;
import nxm.sys.test.junit.NMTestCase;

/** This will test the Args class.

    @author  J. Czechowski IV / Neon Ngo
 */
public final class ArgsTest extends NMTestCase {

  private boolean isGradleBuild = false;
  private Midas M = getMidasContext();

  /** public constructor to allow running this test case directly with JUnit. */
  public ArgsTest (String methodName) { this(methodName, null); }

  /** This constructor is required by NeXtMidas Unit Testing framework.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
   */
  public ArgsTest(String methodName, Midas midas) {
    super(methodName, midas);
    M = (midas!=null) ? midas : Shell.getMidasContext();
    isGradleBuild = Shell.isGradleBuild();
  }

  // Set of matching file names and associated handlers for testing (these may change if the
  // handlers change).
  private static final String[] FILE_NAMES = { urlDat+"apenny.prm",
                                               urlDat+"airports.csv",
                                               urlDat+"Sunset.jpg",
                                               urlDat+"cities.prm",
                                               urlDat+"cities.txt",
                                               urlDat+"network.png",
                                               urlDat+"world.prm",
                                               urlDat+"world.dbf",
                                               urlDat+"world.shp",
                                             };
  private static final Class<?>[]  FILE_TYPES = { DataFile.class,
                                               CsvFile.class,
                                               ImageFile.class,
                                               DataFile.class,
                                               TextFile.class,
                                               ImageFile.class,
                                               DataFile.class,
                                               DbfFile.class,
                                               ShapeFile.class,
                                             };


  //////////////////////////////////////////////////////////////////////////////////////////////////
  // The getFile() test cases
  //////////////////////////////////////////////////////////////////////////////////////////////////
  public void testGetFile () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetD() because fails in Gradle build");
      return;
    }
    for (int i = 0; i < FILE_NAMES.length; i++) {
      String name = FILE_NAMES[i];
      Class<?>  type = FILE_TYPES[i];

      Args args = Shell.parseCommand(getMidasContext(), "FILE;SYS,OPEN,TAG,"+name);

      String   fnIndex = args.getFileName(3).toString();
      String   fnName  = args.getFileName("P1").toString();
      BaseFile bfIndex = args.getFile(3);
      BaseFile bfName  = args.getFile("P1");

      assertEquals("Got expected file using getFile(3) for "+name,  fnIndex, bfIndex.getName().toString());
      assertEquals("Got expected file using getFile(P1) for "+name, fnName, bfName.getName().toString());

      assertEquals("Got expected class using getFile(3) for "+name,  type, bfIndex.getClass());
      assertEquals("Got expected class using getFile(P1) for "+name, type, bfName.getClass());

      if (type.isInstance(ListFile.class)) {
        ListFile lfIndex = args.getFile(3,ListFile.class);
        ListFile lfName  = args.getFile("P1",ListFile.class);

        assertEquals("Got expected file using getFile(3,ListFile.class) for "+name,  fnIndex, lfIndex.getName().toString());
        assertEquals("Got expected file using getFile(P1,ListFile.class) for "+name, fnName, lfName.getName().toString());
        assertTrue("Got instance of ListFile using getFile(3,ListFile.class) for "+name, (lfIndex instanceof ListFile));
        assertTrue("Got instance of ListFile using getFile(P1,ListFile.class) for "+name, (lfName instanceof ListFile));
      }

      if (type.isInstance(PlotFile.class)) {
        PlotFile pfIndex = args.getFile(3,PlotFile.class);
        PlotFile pfName  = args.getFile("P1",PlotFile.class);

        assertEquals("Got expected file using getFile(3,PlotFile.class) for "+name,  fnIndex, pfIndex.getName().toString());
        assertEquals("Got expected file using getFile(P1,PlotFile.class) for "+name, fnName, pfName.getName().toString());
        assertTrue("Got instance of ListFile using getFile(3,PlotFile.class) for "+name, (pfIndex instanceof PlotFile));
        assertTrue("Got instance of ListFile using getFile(P1,PlotFile.class) for "+name, (pfName instanceof PlotFile));
      }

      if (type.isInstance(ImageFile.class)) {
        ImageFile ifIndex = args.getFile(3,ImageFile.class);
        ImageFile ifName  = args.getFile("P1",ImageFile.class);

        assertEquals("Got expected file using getFile(3,ImageFile.class) for "+name,  fnIndex, ifIndex.getName().toString());
        assertEquals("Got expected file using getFile(P1,ImageFile.class) for "+name, fnName, ifName.getName().toString());
        assertTrue("Got instance of ListFile using getFile(3,ImageFile.class) for "+name, (ifIndex instanceof ImageFile));
        assertTrue("Got instance of ListFile using getFile(P1,ImageFile.class) for "+name, (ifName instanceof ImageFile));
      }
    }
  }

  private static enum FavColor { RED, YELLOW, GREEN, BLUE, ORANGE };
  private static enum FavFruit { APPLE, BANANA, ORANGE };

  public void testGetSelection () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetSelection() because fails in Gradle build");
      return;
    }
    // CASE-SENSITIVE
    for (FavColor c : FavColor.values()) {
      Args args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLOR="+c.toString());
      FavColor sel = args.getSelection("/COLOR", FavColor.BLUE);
      assertSame("Got expected selection /COLOR="+c+" (case-sensitive)", c, sel);
    }
    // CASE-INSENSITIVE
    for (FavColor c : FavColor.values()) {
      Args args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLOR=\""+c.toString().toLowerCase()+"\"");
      FavColor sel = args.getSelection("/COLOR", FavColor.BLUE);
      assertSame("Got expected selection /COLOR="+c+" (case-insensitive)", c, sel);
    }
    // DEFAULT VALUE
    {
      Args args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS");
      FavColor sel = args.getSelection("/COLOR", FavColor.BLUE);
      assertSame("Got expected default value for /COLOR=", FavColor.BLUE, sel);
    }

    // DIFFERENT ENUM TYPES
    {
      Args args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLOR=ORANGE");
      FavColor c = args.getSelection("/COLOR", FavColor.BLUE);
      FavFruit f = args.getSelection("/COLOR", FavFruit.APPLE);
      assertSame("Different enum types (FavColor)", FavColor.ORANGE, c);
      assertSame("Different enum types (FavFruit)", FavFruit.ORANGE, f);
    }
  }

  public void testGetMaskValue () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetMaskValue() because fails in Gradle build");
      return;
    }
    MaskValue<FavColor> PRIMARY   = MaskValue.constant(FavColor.RED, FavColor.YELLOW, FavColor.BLUE);
    MaskValue<FavColor> ONLY_BLUE = MaskValue.constant(FavColor.BLUE);
    Args                args;
    MaskValue<FavColor> mask;

    // ENTRY ONLY
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=|RED|YELLOW|GREEN");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, "|RED|YELLOW|GREEN|", mask.toString());

    // NO ENTRY
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, ONLY_BLUE, mask);

    // DEFAULT (with +, -)
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=DEF|+YELLOW|-ORANGE");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, "|YELLOW|BLUE|", mask.toString());

    // ALL (with -)
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=ALL|-ORANGE");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, "|RED|YELLOW|GREEN|BLUE|", mask.toString());

    // NONE (with +)
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=NONE|+RED");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, "|RED|", mask.toString());

    // CURRENT (with ~)
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=~BLUE");
    mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
    assertEquals("Mask value with "+args, "|RED|YELLOW|", mask.toString());

    // INVALID ENTRY
    args = Shell.parseCommand(getMidasContext(), "RESULTS;SYS/COLORS=+MANGO");
    try {
      mask = args.getMaskValue("/COLORS", PRIMARY, ONLY_BLUE);
      assertTrue("Caught exception with "+args, false);
    } catch (IllegalArgumentException e) {
      assertTrue("Caught exception with "+args, true);
    }
  }

  /** @since NeXtMidas 3.1.1 */
  public void testGetString () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetString() because fails in Gradle build");
      return;
    }
    final String defvalue = "outfile.tmp";
    final String defValUpcase = defvalue.toUpperCase();
    final boolean defBool = false;

    Args args = Shell.parseCommand(getMidasContext(), "GET");
    assertEquals("getS(..)     for paramNotPresent",   defValUpcase, args.getS("PARAMNOTPRESENT", defvalue));   // #1A
    assertEquals("getState(..) for paramNotPresent",   defBool,  args.getState("PARAMNOTPRESENT", defBool));    // #1B
    assertEquals("getS(..)     for /switchNotPresent", defValUpcase, args.getS("/SWITCHNOTPRESENT", defvalue)); // #1C
    assertEquals("getState(..) for /switchNotPresent", defBool,  args.getState("/SWITCHNOTPRESENT", defBool));  // #1D

    // *NOTE*: must use a configured command otherwise /emptySwitch will NOT get properly saved in args
    args = Shell.parseCommand(getMidasContext(), "GET/EMPTYSWITCH");
    assertEquals("getU(..)     for empty (LABEL=) param", defValUpcase, args.getU("LABEL", defvalue));        // #2A
    assertEquals("getState(..) for empty (LABEL=) param", false,    args.getState("LABEL"));                  // #2B
    assertEquals("getU(..)     for /emptyswitch",         "",           args.getU("/EMPTYSWITCH", defvalue)); // #2C 3.1.3+, 3.1.0 and prior returns ""
    assertEquals("getU(...)    for /emptyswitch",         defValUpcase, args.getU("/EMPTYSWITCH", null, defvalue)); // #2C 3.1.3 returns defValue to match #1*, #2[AB], #3[AB] (BUG 2568)
    assertEquals("getState(..) for /emptyswitch",         true,     args.getState("/EMPTYSWITCH"));           // #2D TRUE for /stateSwitch

    // *NOTE*: must use a configured command otherwise /switchEqBlank and paramEqBlank will NOT get properly saved in args
    args = Shell.parseCommand(getMidasContext(), "GET/SWITCHEQBLANK= PARAMEQBLANK= ");
    assertEquals("getCS(..)    for paramEqBlank ",  defvalue, args.getCS("PARAMEQBLANK", defvalue));         // #3A
    assertEquals("getState(..) for paramEqBlank ",  false, args.getState("PARAMEQBLANK"));                   // #3B
    assertEquals("getCS(..)    for /switchEqBlank", "",       args.getCS("/SWITCHEQBLANK", defvalue));       // #3C
    assertEquals("getState(..) for /switchEqBlank", true,  args.getState("/SWITCHEQBLANK"));                 // #3D TRUE for /stateSwitch=
    assertEquals("getCS(...)   for /switchEqBlank", "",       args.getCS("/SWITCHEQBLANK", defvalue, "SDEFVAL")); // #3E since 3.1.3
  }
  public void testGetS () {}     // tested in testGetString()
  public void testGetU () {}     // tested in testGetString()
  public void testGetCS () {}    // tested in testGetString()
  public void testGetState () {} // tested in testGetString()

  public void testGetD () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetD() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Double  TEN  = 10.0;
    Double  TWO  =  2.0;

    // Numbers
    assertEquals("getD for /NONE    def=n/a         ", -1d,                 args.getD("/NONE"));
    assertEquals("getD for /EMPTY1  def=n/a         ", 1d,                  args.getD("/EMPTY1"));
    assertEquals("getD for /EMPTY2  def=n/a         ", 1d,                  args.getD("/EMPTY2"));
    assertEquals("getD for /SWITCH  def=n/a         ", 42.75,               args.getD("/SWITCH"));
    assertEquals("getD for VALUE    def=n/a         ", 3.14,                args.getD("VALUE"));
    assertEquals("getD for /NONE    def=10          ", 10.0,                args.getD("/NONE",   10.0));
    assertEquals("getD for /EMPTY1  def=10          ", 1d,                  args.getD("/EMPTY1", 10.0));
    assertEquals("getD for /EMPTY2  def=10          ", 1d,                  args.getD("/EMPTY2", 10.0));
    assertEquals("getD for /SWITCH  def=10          ", 42.75,               args.getD("/SWITCH", 10.0));
    assertEquals("getD for VALUE    def=10          ", 3.14,                args.getD("VALUE",   10.0));
    // Objects
    assertSame(  "getD for /NONE    def=10  sdef=n/a", TEN,                 args.getD("/NONE",   TEN));
    assertSame(  "getD for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_D, args.getD("/EMPTY1", TEN));
    assertSame(  "getD for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_D, args.getD("/EMPTY2", TEN));
    assertEquals("getD for /SWITCH  def=10  sdef=n/a", (Number)42.75,       args.getD("/SWITCH", TEN));
    assertEquals("getD for VALUE    def=10  sdef=n/a", (Number)3.14,        args.getD("VALUE",   TEN));
    assertSame(  "getD for /NONE    def=10  sdef=2",   TEN,                 args.getD("/NONE",   TEN, TWO));
    assertSame(  "getD for /EMPTY1  def=10  sdef=2",   TWO,                 args.getD("/EMPTY1", TEN, TWO));
    assertSame(  "getD for /EMPTY2  def=10  sdef=2",   TWO,                 args.getD("/EMPTY2", TEN, TWO));
    assertEquals("getD for /SWITCH  def=10  sdef=2",   (Number)42.75,       args.getD("/SWITCH", TEN, TWO));
    assertEquals("getD for VALUE    def=10  sdef=2",   (Number)3.14,        args.getD("VALUE",   TEN, TWO));
  }

  public void testGetF () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetF() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Float   TEN  = 10.0f;
    Float   TWO  =  2.0f;

    // Numbers
    assertEquals("getF for /NONE    def=n/a         ", -1f,                 args.getF("/NONE"));
    assertEquals("getF for /EMPTY1  def=n/a         ", 1f,                  args.getF("/EMPTY1"));
    assertEquals("getF for /EMPTY2  def=n/a         ", 1f,                  args.getF("/EMPTY2"));
    assertEquals("getF for /SWITCH  def=n/a         ", 42.75f,              args.getF("/SWITCH"));
    assertEquals("getF for VALUE    def=n/a         ", 3.14f,               args.getF("VALUE"));
    assertEquals("getF for /NONE    def=10          ", 10.0f,               args.getF("/NONE",   10.0f));
    assertEquals("getF for /EMPTY1  def=10          ", 1f,                  args.getF("/EMPTY1", 10.0f));
    assertEquals("getF for /EMPTY2  def=10          ", 1f,                  args.getF("/EMPTY2", 10.0f));
    assertEquals("getF for /SWITCH  def=10          ", 42.75f,              args.getF("/SWITCH", 10.0f));
    assertEquals("getF for VALUE    def=10          ", 3.14f,               args.getF("VALUE",   10.0f));
    // Objects
    assertSame(  "getF for /NONE    def=10  sdef=n/a", TEN,                 args.getF("/NONE",   TEN));
    assertSame(  "getF for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_F, args.getF("/EMPTY1", TEN));
    assertSame(  "getF for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_F, args.getF("/EMPTY2", TEN));
    assertEquals("getF for /SWITCH  def=10  sdef=n/a", (Number)42.75f,      args.getF("/SWITCH", TEN));
    assertEquals("getF for VALUE    def=10  sdef=n/a", (Number)3.14f,       args.getF("VALUE",   TEN));
    assertSame(  "getF for /NONE    def=10  sdef=2",   TEN,                 args.getF("/NONE",   TEN, TWO));
    assertSame(  "getF for /EMPTY1  def=10  sdef=2",   TWO,                 args.getF("/EMPTY1", TEN, TWO));
    assertSame(  "getF for /EMPTY2  def=10  sdef=2",   TWO,                 args.getF("/EMPTY2", TEN, TWO));
    assertEquals("getF for /SWITCH  def=10  sdef=2",   (Number)42.75f,      args.getF("/SWITCH", TEN, TWO));
    assertEquals("getF for VALUE    def=10  sdef=2",   (Number)3.14f,       args.getF("VALUE",   TEN, TWO));
  }

  /**
   * Tests the three getList methods in Args
   */
  public void testGetList () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetList() because fails in Gradle build");
      return;
    }

    Args args = Shell.parseCommand(getMidasContext(),"RESULT MYDATA (1,2,3,4)");
    List<Double> myList = new ArrayList<>();
    myList.add(1.0);
    myList.add(2.0);
    myList.add(3.0);
    myList.add(4.0);
    List<String> defList = new ArrayList<>();
    defList.add("default");
    defList.add("list");

    assertEquals("getList for key VALUE ",args.getList("VALUE"),myList);
    assertEquals("getList for argument 2 ",args.getList(2),myList);
    //test default list
    assertEquals("getList for key VALUE, default list set",args.getList("VALUE",defList),myList);
    assertEquals("getList for key BOGUS, default list returned ",args.getList("BOGUS",defList),defList);

  }

  public void testGetX () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetX() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Long    TEN  = 10L;
    Long    TWO  =  2L;

    // Numbers
    assertEquals("getX for /NONE    def=n/a         ", -1L,                 args.getX("/NONE"));
    assertEquals("getX for /EMPTY1  def=n/a         ", 1L,                  args.getX("/EMPTY1"));
    assertEquals("getX for /EMPTY2  def=n/a         ", 1L,                  args.getX("/EMPTY2"));
    assertEquals("getX for /SWITCH  def=n/a         ", 42L,                 args.getX("/SWITCH"));
    assertEquals("getX for VALUE    def=n/a         ", 3L,                  args.getX("VALUE"));
    assertEquals("getX for /NONE    def=10          ", 10L,                 args.getX("/NONE",   10L));
    assertEquals("getX for /EMPTY1  def=10          ", 1L,                  args.getX("/EMPTY1", 10L));
    assertEquals("getX for /EMPTY2  def=10          ", 1L,                  args.getX("/EMPTY2", 10L));
    assertEquals("getX for /SWITCH  def=10          ", 42L,                 args.getX("/SWITCH", 10L));
    assertEquals("getX for VALUE    def=10          ", 3L,                  args.getX("VALUE",   10L));
    // Objects
    assertSame(  "getX for /NONE    def=10  sdef=n/a", TEN,                 args.getX("/NONE",   TEN));
    assertSame(  "getX for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_X, args.getX("/EMPTY1", TEN));
    assertSame(  "getX for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_X, args.getX("/EMPTY2", TEN));
    assertEquals("getX for /SWITCH  def=10  sdef=n/a", (Number)42L,         args.getX("/SWITCH", TEN));
    assertEquals("getX for VALUE    def=10  sdef=n/a", (Number)3L,          args.getX("VALUE",   TEN));
    assertSame(  "getX for /NONE    def=10  sdef=2",   TEN,                 args.getX("/NONE",   TEN, TWO));
    assertSame(  "getX for /EMPTY1  def=10  sdef=2",   TWO,                 args.getX("/EMPTY1", TEN, TWO));
    assertSame(  "getX for /EMPTY2  def=10  sdef=2",   TWO,                 args.getX("/EMPTY2", TEN, TWO));
    assertEquals("getX for /SWITCH  def=10  sdef=2",   (Number)42L,         args.getX("/SWITCH", TEN, TWO));
    assertEquals("getX for VALUE    def=10  sdef=2",   (Number)3L,          args.getX("VALUE",   TEN, TWO));
  }

  public void testGetL () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetL() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Integer TEN  = 10;
    Integer TWO  =  2;

    // Numbers
    assertEquals("getL for /NONE    def=n/a         ", -1,                  args.getL("/NONE"));
    assertEquals("getL for /EMPTY1  def=n/a         ", 1,                   args.getL("/EMPTY1"));
    assertEquals("getL for /EMPTY2  def=n/a         ", 1,                   args.getL("/EMPTY2"));
    assertEquals("getL for /SWITCH  def=n/a         ", 42,                  args.getL("/SWITCH"));
    assertEquals("getL for VALUE    def=n/a         ", 3,                   args.getL("VALUE"));
    assertEquals("getL for /NONE    def=10          ", 10,                  args.getL("/NONE",   10));
    assertEquals("getL for /EMPTY1  def=10          ", 1,                   args.getL("/EMPTY1", 10));
    assertEquals("getL for /EMPTY2  def=10          ", 1,                   args.getL("/EMPTY2", 10));
    assertEquals("getL for /SWITCH  def=10          ", 42,                  args.getL("/SWITCH", 10));
    assertEquals("getL for VALUE    def=10          ", 3,                   args.getL("VALUE",   10));
    // Objects
    assertSame(  "getL for /NONE    def=10  sdef=n/a", TEN,                 args.getL("/NONE",   TEN));
    assertSame(  "getL for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_L, args.getL("/EMPTY1", TEN));
    assertSame(  "getL for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_L, args.getL("/EMPTY2", TEN));
    assertEquals("getL for /SWITCH  def=10  sdef=n/a", (Number)42,          args.getL("/SWITCH", TEN));
    assertEquals("getL for VALUE    def=10  sdef=n/a", (Number)3,           args.getL("VALUE",   TEN));
    assertSame(  "getL for /NONE    def=10  sdef=2",   TEN,                 args.getL("/NONE",   TEN, TWO));
    assertSame(  "getL for /EMPTY1  def=10  sdef=2",   TWO,                 args.getL("/EMPTY1", TEN, TWO));
    assertSame(  "getL for /EMPTY2  def=10  sdef=2",   TWO,                 args.getL("/EMPTY2", TEN, TWO));
    assertEquals("getL for /SWITCH  def=10  sdef=2",   (Number)42,          args.getL("/SWITCH", TEN, TWO));
    assertEquals("getL for VALUE    def=10  sdef=2",   (Number)3,           args.getL("VALUE",   TEN, TWO));
  }

  public void testGetI () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetI() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Short   TEN  = (short)10;
    Short   TWO  = (short) 2;

    // Numbers
    assertEquals("getI for /NONE    def=n/a         ", (short)-1,           args.getI("/NONE"));
    assertEquals("getI for /EMPTY1  def=n/a         ", (short)1,            args.getI("/EMPTY1"));
    assertEquals("getI for /EMPTY2  def=n/a         ", (short)1,            args.getI("/EMPTY2"));
    assertEquals("getI for /SWITCH  def=n/a         ", (short)42,           args.getI("/SWITCH"));
    assertEquals("getI for VALUE    def=n/a         ", (short)3,            args.getI("VALUE"));
    assertEquals("getI for /NONE    def=10          ", (short)10,           args.getI("/NONE",   (short)10));
    assertEquals("getI for /EMPTY1  def=10          ", (short)1,            args.getI("/EMPTY1", (short)10));
    assertEquals("getI for /EMPTY2  def=10          ", (short)1,            args.getI("/EMPTY2", (short)10));
    assertEquals("getI for /SWITCH  def=10          ", (short)42,           args.getI("/SWITCH", (short)10));
    assertEquals("getI for VALUE    def=10          ", (short)3,            args.getI("VALUE",   (short)10));
    // Objects
    assertSame(  "getI for /NONE    def=10  sdef=n/a", TEN,                 args.getI("/NONE",   TEN));
    assertSame(  "getI for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_I, args.getI("/EMPTY1", TEN));
    assertSame(  "getI for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_I, args.getI("/EMPTY2", TEN));
    assertEquals("getI for /SWITCH  def=10  sdef=n/a", (Number)(short)42,   args.getI("/SWITCH", TEN));
    assertEquals("getI for VALUE    def=10  sdef=n/a", (Number)(short)3,    args.getI("VALUE",   TEN));
    assertSame(  "getI for /NONE    def=10  sdef=2",   TEN,                 args.getI("/NONE",   TEN, TWO));
    assertSame(  "getI for /EMPTY1  def=10  sdef=2",   TWO,                 args.getI("/EMPTY1", TEN, TWO));
    assertSame(  "getI for /EMPTY2  def=10  sdef=2",   TWO,                 args.getI("/EMPTY2", TEN, TWO));
    assertEquals("getI for /SWITCH  def=10  sdef=2",   (Number)(short)42,   args.getI("/SWITCH", TEN, TWO));
    assertEquals("getI for VALUE    def=10  sdef=2",   (Number)(short)3,    args.getI("VALUE",   TEN, TWO));
  }

  public void testGetB () {
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testGetB() because fails in Gradle build");
      return;
    }
    Args    args = Shell.parseCommand(getMidasContext(), "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14");
    Byte    TEN  = (byte)10;
    Byte    TWO  = (byte) 2;

    // Numbers
    assertEquals("getB for /NONE    def=n/a         ", (byte)-1,            args.getB("/NONE"));
    assertEquals("getB for /EMPTY1  def=n/a         ", (byte)1,             args.getB("/EMPTY1"));
    assertEquals("getB for /EMPTY2  def=n/a         ", (byte)1,             args.getB("/EMPTY2"));
    assertEquals("getB for /SWITCH  def=n/a         ", (byte)42,            args.getB("/SWITCH"));
    assertEquals("getB for VALUE    def=n/a         ", (byte)3,             args.getB("VALUE"));
    assertEquals("getB for /NONE    def=10          ", (byte)10,            args.getB("/NONE",   (byte)10));
    assertEquals("getB for /EMPTY1  def=10          ", (byte)1,             args.getB("/EMPTY1", (byte)10));
    assertEquals("getB for /EMPTY2  def=10          ", (byte)1,             args.getB("/EMPTY2", (byte)10));
    assertEquals("getB for /SWITCH  def=10          ", (byte)42,            args.getB("/SWITCH", (byte)10));
    assertEquals("getB for VALUE    def=10          ", (byte)3,             args.getB("VALUE",   (byte)10));
    // Objects
    assertSame(  "getB for /NONE    def=10  sdef=n/a", TEN,                 args.getB("/NONE",   TEN));
    assertSame(  "getB for /EMPTY1  def=10  sdef=n/a", Args.STATE_SWITCH_B, args.getB("/EMPTY1", TEN));
    assertSame(  "getB for /EMPTY2  def=10  sdef=n/a", Args.STATE_SWITCH_B, args.getB("/EMPTY2", TEN));
    assertEquals("getB for /SWITCH  def=10  sdef=n/a", (Number)(byte)42,    args.getB("/SWITCH", TEN));
    assertEquals("getB for VALUE    def=10  sdef=n/a", (Number)(byte)3,     args.getB("VALUE",   TEN));
    assertSame(  "getB for /NONE    def=10  sdef=2",   TEN,                 args.getB("/NONE",   TEN, TWO));
    assertSame(  "getB for /EMPTY1  def=10  sdef=2",   TWO,                 args.getB("/EMPTY1", TEN, TWO));
    assertSame(  "getB for /EMPTY2  def=10  sdef=2",   TWO,                 args.getB("/EMPTY2", TEN, TWO));
    assertEquals("getB for /SWITCH  def=10  sdef=2",   (Number)(byte)42,    args.getB("/SWITCH", TEN, TWO));
    assertEquals("getB for VALUE    def=10  sdef=2",   (Number)(byte)3,     args.getB("VALUE",   TEN, TWO));
  }

  public void testGetRawArgs () {
    boolean plotCmdExists = getMidas().dictionary.isDictionaryEntry("PLOT");
    if (plotCmdExists) {
      String[] TEST_CASES = {
        // INPUT                                                       RAW ARGS
        "SET/EMPTY1/EMPTY2=/SWITCH=42.75 FOO 3.14",                    "FOO 3.14",
        "SET/EMPTY1/EMPTY2=/SWITCH=42.75,FOO,3.14",                    "FOO,3.14",
        "PLOT/BG/WTITLE=\"My Plot\" apenny.prm",                       "apenny.prm",
        "PLOT/BG/WTITLE=\"My Plot\",apenny.prm",                       "apenny.prm",
        "PLOT/BG/WTITLE=\"My, Way Cool, Plot\" apenny.prm",            "apenny.prm",
        "PLOT/BG/WTITLE=\"My, Way Cool, Plot\",apenny.prm",            "apenny.prm",
        "PLOT/BG/WTITLE=\"My Plot\" /home/xmmgr/apenny.prm /ALL",      "/home/xmmgr/apenny.prm",
        "PLOT/BG/WTITLE=\"My Plot\",/home/xmmgr/apenny.prm /ALL",      "/home/xmmgr/apenny.prm",
        "PLOT/BG/THEME={CWBG=RED,CWFG=BLUE} apenny.prm",               "apenny.prm",
        "PLOT/BG/THEME={CWBG=RED,CWFG=BLUE},apenny.prm",               "apenny.prm",
        "ASSERT/TEXT=\"some junk text\"/TEST    1    eq    1",         "   1    eq    1",
        "ASSERT/TEXT=\"some junk text\"/TEST 1 eq 1",                  "1 eq 1",
        "ASSERT/TEXT=\"some junk text\"/TEST,1,eq,1",                  "1,eq,1",
        "ASSERT/TEXT=\"some junk text\"/TEST 1 eq 1 /DEBUG",           "1 eq 1",
        "ASSERT/TEXT=\"some junk text\"/TEST,1,eq,1,/DEBUG",           "1,eq,1",
        "ASSERT/TEXT=\"some junk text\"/TEST 1 eq 1 /DEBUG=TRUE",      "1 eq 1",
        "ASSERT/TEXT=\"some junk text\"/TEST,1,eq,1,/DEBUG=TRUE",      "1,eq,1",
        "ASSERT/TEXT=\"my test\"/TEST 3.14 eq 3.14159265 /DELTA=0.01", "3.14 eq 3.14159265",
        "ASSERT/TEXT=\"my test\"/TEST,3.14,eq,3.14159265,/DELTA=0.01", "3.14,eq,3.14159265",
        "TIMEX/PREC=( 2 ) now",                                        "now",

        // OS commands are special-case since they do not have switches even if it
        // looks like they do, they also have special handling when using the '$'.
        "OS cd /tmp",                                                  "cd /tmp",
        "$cd /tmp",                                                    "cd /tmp",
        "$ls -al",                                                     "ls -al",
      };

      for (int i = 0; i < TEST_CASES.length; i+=2) {
        Args   args = Shell.parseCommand(getMidasContext(), TEST_CASES[i]);
        String exp  = TEST_CASES[i+1];
        String act  = args.getRawArgs();

        assertEquals("getRawArgs() for '"+TEST_CASES[i]+"'", exp, act);
      }
    }
    else {
      info("Skipping getRawArgs(..) test, seem to be in LIBNXM mode without PLOT command");
    }
  }

  public void testParseArgs () {}  // tested in testParseCommand ()

  public void testParseCommand () {

    Midas m = getMidasContext().cloneOf();
    // since 3.5.4 - next three lines setup to capture warn messages to a "LOG" message(s) via the Midas.msglog mechanism
    MessageQueue mq = new MessageQueue(m, this);
    String msgid = Util.getClassName(getClass()).toUpperCase();
    msgid = m.registry.putInstance(msgid, mq);
    m.msglog = msgid;

    Args   args = Shell.parseCommand(m,"noop "+urlDat+"sinewave.prm ram:data");
    assertContainsWarnMsg(mq, "No duplicate keys warning issued for NOOP command", null);

    m.info("5 Warnings for 'Duplicate keys entered...' are expected below");

    args = Shell.parseCommand(m,"headermod ram:data XSTART=123 TYPE=2000 FS=32768 XSTART=456");
    assertContainsWarnMsg(mq, "Duplicate keys warning issued for keys XSTART", "Duplicate keys entered");

    args = Shell.parseCommand(m, "plot apenny X1=2 X1=20");
    assertContainsWarnMsg(mq, "Duplicate keys warning issued for keys X1", "Duplicate keys entered");

    args = Shell.parseCommand(m,"calc x 3.0 4 +");
    assertContainsWarnMsg(mq, "No duplicate keys warning issued for CALC", null);

    args = Shell.parseCommand(m,"headermod ram:data TYPE=2000 FS=32768 XSTART=456 XSTART=123");
    assertContainsWarnMsg(mq, "Duplicate keys warning issued for keys XSTART", "Duplicate keys entered");

    args = Shell.parseCommand(m, "plot/CMAP=GrayScale apenny X1=2 X1=20 CM=20Log");
    assertContainsWarnMsg(mq, "Duplicate keys warning issued for keys X1", "Duplicate keys entered");

    args = Shell.parseCommand(m, "plot/CMAP=GrayScale apenny X1=2 X1=20 CM=20Log /CMAP=GrayScale");
    assertContainsWarnMsg(mq, "Duplicate keys warning issued for keys X1, not for duplicate switches CMAP", "Duplicate keys entered");

    args = Shell.parseCommand(m, "plot/CM=20Log apenny X1=2 Y1=20 CM=20Log");
    assertContainsWarnMsg(mq, "No duplicate keys warning issued for switch and key CM", null);

    args = Shell.parseCommand(m, "plot/CM=20Log apenny X1=2 Y1=20 CM=20Log /CM=20Log");
    assertContainsWarnMsg(mq, "No duplicate keys warning issued for switches and key CM", null);

  }

  /**
  @param mq The message queue that got the message were routed to
  @param assertMessage  A description of the test.
  @param contains "Some" of the text expected in the warning message.
  @since NeXtMidas 3.5.4
*/
  private void assertContainsWarnMsg (MessageQueue mq, final String assertMessage, final String contains) {
    Message msg = mq.get();

    while (msg != null) {
      if ("LOG".equals(msg.getName()) && msg.getInfo() == Midas.WARNING) {
        if (contains == null) { // should not get any warning messages
          fail(assertMessage);
        }
        Object msgData = msg.getData();
        if (! (msgData != null && msgData.toString().contains(contains))) {
          fail(assertMessage + " contains=" + contains + " actual=" + msgData);
        }
      }
      msg = mq.get();
    }

    assertTrue(assertMessage, true);
  }

  /** Tests the Args instanceof DataFile code added a part of Jeff's SaaS changes
   *  NXM-2755 / 3.7.1
   *  @since NeXtMidas 3.9.4
   */
  public void testChildDataFile() {
    Midas M = getMidasContext();
    if (isGradleBuild) {
      M.warning("nxm.sys.junit.lib.ArgsTest skipping testChildDataFile() because fails in Gradle build");
      return;
    }
    String cldName = "ChildDataFile1000{AUX=RAM}";
    File file =  new File(cldName);
    nxm.sys.test.ChildDataFile cldDf = new nxm.sys.test.ChildDataFile(M,file,"1000","CF",DataFile.OUTPUT,"hello");
    cldDf.open(DataFile.OUTPUT);
    Keywords cldDfKw = cldDf.getKeywordsObject();
    cldDfKw.setKey("S:TESTKEY", "This is a test");
    Data newFirstElem = new Data(0.25,0.5);
    Data newSecondElem = new Data(1.25,1.5);
    cldDf.setData(0,newFirstElem);
    cldDf.setData(1,newSecondElem);
    cldDf.close();

    String cldNoopName = "ChildDataFile1000Noop{AUX=RAM}";
    String noopCmd = "NOOP " + cldName + " " + cldNoopName;
    Shell.runCommand(M, noopCmd);

    DataFile cldDfNoop = new DataFile(M, cldNoopName);
    cldDfNoop.open(DataFile.INPUT);
    Keywords cldDfNoopKw = cldDfNoop.getKeywordsObject();
    Object cldDfNoopKwTest = cldDfNoopKw.getKey("TESTKEY");
    assertTrue("The file " + cldDfNoop.getName() + " contains the test key", cldDfNoopKwTest != null);
    cldDfNoop.close();
  }
}
