package nxm.sys.test;

import javax.swing.JFrame;
import javax.swing.JPanel;

import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Command;
import nxm.sys.lib.Data;
import nxm.sys.lib.Midas;
import nxm.sys.lib.NTerminal;
import nxm.sys.lib.Native;
import nxm.sys.lib.NeXtMidas;
import nxm.sys.lib.NeXtMidas.OptionFlags;
import nxm.sys.lib.OptionTree;
import nxm.sys.lib.Shell;
import nxm.sys.lib.Table;
import nxm.sys.libg.MidasDisplay;
import nxm.sys.test.junit.NMTestCase;


/** A class to test running NeXtMidas commands from outside of a Midas context

    @author J. Bauer
    @since NeXtMidas 2.9.0
 */
public class TestNeXtMidas  {

  private String argAuxWriteHome   = "-aux.write=HOME";
  private String argAuxReadHomeDat = "-aux.read=HOME|DAT";
  private String argPathSysUcl     = "-path=DSP|SYS";
  private String cmdPlotSunset     = "-cmd=plot/all Sunset.jpg";  // should be able to find Sunset, includes test for Bug 2748
  private String cmdMakeUCL        = "make code ucl";
  private String cmdRunBeanDemo    = "beandemo";  // should be able to find cities.prm;
  private String cmdSetHomeDSP     = "home test DSP";
  private String cmdRunDSPTest     = "%test_convolve";
  private String cmdPlotWorldBG    = "plot/bg/id=myplotid world";  // should be able to find world.prm
  private String cmdList2Apenny    = "list2/bg/wpos=(25,600,600,200) apenny ";
  private String cmdList2UCCSV     = "list2/bg/wpos=(25,600,600,200) CSV_UPPERCASE.CSV{HEADERROWS=1}";
  private String cmdPlotApenny     = "plot apenny";   // last one run so not background-ed
  private String cmdSD360          = "sd360 inf";     // last one run so not background-ed
  private Table  argsTable         = new Table("{cmd=home test dat,cmd=home test sys,cmd=\"plot sinewave\"}");
  private String cmdHomeTestSys    = "-cmd=home test sys";
  private String scriptCmd         = "-script=nxm.sys.test.test_plot.mm";
  private String cmdPlotSinewave   = "-cmd=plot/bg sinewave";
  private String cmdSetAllSwing    = "env set graphics swing|swingwidgets|swingpopups";
  private String argNewAux         = "-aux.test_nxm=/tmp/bogus";
  private String cmdCheckAux       = "res aux.test_nxm";
  final JFrame frame = new JFrame("NeXtMidas");  // the frame (window) to use.
  final JPanel panel = new JPanel();   // the content pane for the frame.

  private NeXtMidas nxm;
  private NeXtMidas nxmJava;

  /** Constructor was modified to support the original
   *  TestNextMidas and the new NXM-2900 issue with defining path/options
   *
   *  @param testName An empty string or a test function name
   */
  public TestNeXtMidas(String testName) {
    if (testName.equalsIgnoreCase("addOptToPathFromClasspath")) {
      createNXM();
      testAddOptToPathfromClasspath();
    // for Internal testing only
    }
    else if (testName.equalsIgnoreCase("addOptToPathUsingSPI")) {
      createNXM();
      doOptToPathUsingSPI();
    }
    else if (testName.equalsIgnoreCase("loadNativeCodeFromJar")) {
     createNXM();
     testLoadingNativeCode();
    }
    else if (testName.equalsIgnoreCase("JavaApp")) {
      testJavaApp();
    }
    else {
    testNoDupsInHandlersTable(); // since 3.1.2: keep this FIRST for testing BUG 2596
    testOneShot();
    createNXM();
    testInstantiate();
    testScript();
    testPanel();
    }
  }

  /** Initialize new instance */
  public TestNeXtMidas() {
    this("");
  }

  /**
   * Main method was modify to accept command line arguments
   * fix 1a. NXM-2900 Issues defining path/options
   *
   * @param args  The name of the test to run in this case "addOptToPathFromClasspath"
   */
  public static void main (String[] args) {
    if (args.length == 0) new TestNeXtMidas("");
    else new TestNeXtMidas(args[0]);
  }

  /* To be able to use the following SPI method. Manual changes
   * will need to be performed by the developer.
   *
   *  1. Run the nm make all command for building NextMidas
   *  2. Copy the following directories to your home area.
   *     cp -rf ~/nxm37x/optiontrees ~/
   *     cp -rf ~/nxn37x/META-INF ~/META<opt>
   *
   *  3. Compile the java code within the optiontrees.
   *     javac -cp ~/nxm37x/jars/nxm-sys.jar optiontrees/*.java
   *  4. Copy the nxm-<opt>.jar file to your home area from the NeXtMidas jars directory
   *     cp ~/nxm37x/jars/nxm-<opt>.jar ~/
   *  5. Uncompress the jar file nxm-<opt>.jar
   *     jar xf nxm-<opt>.jar
   *  6. cd to ~/META-INF directory and remove all the files
   *     rm MANIFEST.MG NMSIGN.DSA NMSIGN.SF
   *  7. cp -rf ~/META<opt>/services ~/META-INF
   *  8. Create new nxm-<opt>.jar
   *     jar cvf nxm-<opt>.jar META-INF nxm/ optiontrees/
   *  9. Overwrite old jar file with new one.
   *     cp nxm-<opt>.jar ~/nxm37x/jars/
   * 10. Run the example as follow:
   *     java -DNONATIVE=TRUE -DNMSTARTUP.USER=nxm.sys.test.custom_startup.mm -classpath ~/nxm37x/jars/nxm-dsp.jar:~/nxm37x/jars/nxm-sys.jar nxm.sys.test.TestNeXtMidas addOptToPathUsingSPI
   */
  /** To be able to use the following SPI method. Manual changes
   *  will need to be performed by the developer.
   *  @since NeXtMidas 3.7.1
   */
  @InternalUseOnly
  public void doOptToPathUsingSPI () {
    String pathName = nxm.addOptionTreePathUsingSPI();
    Table optTbl = OptionTree.getAllOptions(Shell.getMidasContext());
    System.out.println("Option Tree Table: "+optTbl);
    System.out.println("Option path: "+pathName);
  }

  /**
   * Method for testing 1a. NXM-2900 Issues defining path/options
   */
  public void testAddOptToPathfromClasspath () {
    Object[][] optExpectedPath = {
        {new String[] {"MYTESTOPT1","MYTESTOPT1,DSP,SYS"}, OptionFlags.ADD },
        {new String[] {"DSP,MYTESTOPT2","MYTESTOPT1,DSP,MYTESTOPT2,SYS"}, OptionFlags.ADD_PLUS},
        {new String[] {"MYTESTOPT2","MYTESTOPT1,DSP,MYTESTOPT2,SYS"}, OptionFlags.PUT},
    };

    for (Object[] optPathSet : optExpectedPath){
      String[] optionSet = (String[])optPathSet[0];
      OptionFlags optflag = (OptionFlags)optPathSet[1];
      nxm.addOptionTreeToPathClasspath(optionSet[0], optflag);
      nxm.runCommand("assert/text=\"Add Option "+optionSet[0]+" with option flag: "+optflag+" \" \""+optionSet[1]+"\" EQS \""+nxm.getPath()+"\" ");
    }
    Table optTbl = OptionTree.getAllOptions(Shell.getMidasContext());
    nxm.runCommand("assert/text=\"Option added DSP \" "+optTbl.containsKey("DSP")+" ISTRUE");
    nxm.runCommand("assert/text=\"Option added SYS \" "+optTbl.containsKey("SYS")+" ISTRUE");
  }

  private void testLoadingNativeCode () {
    // load a second library from the jar ... this is what causes the Native load to intermittently fail
    Midas M = Shell.getSharedMidasContext();

    // Load Native code for NTerminal
    if (Native.useIO) {
      try {
        new NTerminal (M);
      } catch (Exception e) {
        M.printStackTrace("Error loading native terminal, may not get accurate reulsts from test.", e);
      }
    }

    // set up for potential failure - if Native did not load
    String format = "CF";
    int size = 6; // NOTE: If BUG 2433 is not resolved, keep size even or change format to CD
    float[] array = {42, 789, 234, 69};
    Data dat1 = new Data(format, size);
    Data fiveData = new Data((float)5.0);
    dat1.setScalarAt(4, array);    //byte offset
    dat1.setScalarAt(12, fiveData);

    try {
      dat1.castF(true);
    }
    catch (ClassCastException cce) {
      NMTestCase.fail("\n  Native code did not load properly from jar resulting in the following Exception:\n   "+cce+"/n");
    }
  }

  /** 1st test is running with no instantiation. Command and args passed in */
  private void testOneShot () {
    String[] args = { argAuxWriteHome,
                      argAuxReadHomeDat,
                      argPathSysUcl,
                      cmdPlotSunset,
                    };
    NeXtMidas.main(args);
  }

  /** 2nd test is to instantiate then run a command */
  private void testInstantiate () {
    if (nxm == null) return;
    //nxm.runCommand(cmdMakeUCL);  // TODO: add this back when this works
    //nxm.runCommand(cmdRunBeanDemo);
    nxm.runCommand(cmdCheckAux);
    nxm.runCommand(cmdSetHomeDSP);
    nxm.runCommand(cmdRunDSPTest);
    nxm.runCommand("home test sys");
   // nxm.runCommand(cmdList2Apenny);
  }

  private void createNXM () {
    String[] args = { argAuxWriteHome, argAuxReadHomeDat, argPathSysUcl, argNewAux };

    nxm = new NeXtMidas(args);
  }

  private void closeNXM () {
    // add later ??
    nxm = null;
  }

  /** Tests pushing the graphics onto a given panel */
  @SuppressWarnings("deprecation")  private void testPanel () {
    if (nxm == null) return;

    panel.setLayout(new java.awt.BorderLayout());
    frame.setContentPane(panel);
    frame.setSize(900, 900);

    // set up a MidasDisplay in the top part of the frame
    MidasDisplay md1        = new MidasDisplay();
    java.awt.Dimension dim  = new java.awt.Dimension(900,300);
    md1.setPreferredSize(dim);
    panel.add(md1,java.awt.BorderLayout.NORTH);

    // set up another MidasDisplay in the bottom part of the frame
    MidasDisplay md2 = new MidasDisplay();
    md2.setPreferredSize(dim);
    panel.add(md2,java.awt.BorderLayout.SOUTH);

    // set up a MidasDisplay in the middle part of the frame
    MidasDisplay md3 = new MidasDisplay();
    md3.setPreferredSize(dim);
    panel.add(md3,java.awt.BorderLayout.CENTER);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

    Command cmd1Plot  = nxm.runCommand(cmdPlotWorldBG, md1); // test new API in 3.3.0
    Command cmd2List2 = nxm.runCommand(cmdList2Apenny, md2); // test new API in 3.3.0
    nxm.runCommand("assert/text=\"runCommand returns executed Command reference: Plot\" "+(cmd1Plot != null)+" ISTRUE");
    nxm.runCommand("assert/text=\"runCommand returns executed Command reference: List2\" "+(cmd2List2 != null)+" ISTRUE");
    nxm.runCommand("assert/text=\"PLOT cmd's ID\" "+cmd1Plot.getID()+" EQS MYPLOTID");
    nxm.runCommand("assert/text=\"LIST2 cmd's ID\" "+cmd2List2.getID()+" EQS LIST2");

    nxm.runCommand(cmdSD360, md3); // use 3.3.0 API
  }

  private void testJavaApp () {
    Midas.setForceNmJavaMode("AUTO");
    nxmJava = new NeXtMidas();
    nxmJava.runCommand(cmdList2UCCSV);
    testMidasIsJava();
  }

  /** NXM-4097 Test that Midas knows it is a Java App */
  private void testMidasIsJava () {
    boolean globalIsJava = NeXtMidas.getGlobalInstance().getMidas().isJavaBehavior();
    boolean shellIsJava  = Shell.getMidasContext().isJavaBehavior();
    nxmJava.runCommand("assert/text=\"Global Midas recognizes this is a Java application\" "+globalIsJava+" ISTRUE");
    nxmJava.runCommand("assert/text=\"Shell Midas recognizes this is a Java application\" "+shellIsJava+" ISTRUE");
  }

  /** test running with the -script option */
  private void testScript () {
    if (nxm == null) return;
  //  nxm.parseOption(cmd6); // parse a command as an option
    nxm.parseOptions(argsTable); // parse options in Table form
    nxm.parseOption(scriptCmd);
    nxm.runShell();
  }

  /** automatic test for BUG 2596 REG.HANDLERS.PACKET in -INDEPENDENT mode allows dupes.
      @since NeXtMidas 3.1.2
   */
  private void testNoDupsInHandlersTable () {
    String[] args = {"-script=nxm.sys.test.test_dr814551-1.mm"};
    NeXtMidas.main(args);
  }
}