package nxm.sys.test.junit.lib;

import nxm.sys.inc.MapKBT;
import nxm.sys.lib.*;
import nxm.sys.lib.XmlFile.Xml2TblConversionOptions;
import nxm.sys.test.junit.NMTestCase;

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

/** This will test the XmlFile class.
    Originally created in response to Bug 1595
    @author Jim Bauer
    @since NeXtMidas 2.6.1
 */
public final class XmlFileTest extends NMTestCase {

  /** String used as sample input or expected results data for conversions */
  private final String sampleTableStringWithCNameCompressionFalse =
    "{ROW_0={ID={TESTDATA=\"1\"},DATE={TESTDATA=\"2008-07-18\"},ITEM=" +
    "{TESTDATA=\"GOOD Morning\"}},ROW_1={ID={TESTDATA=\"2\"},DATE=" +
    "{TESTDATA=\"2008-07-24\"},ITEM={TESTDATA=\"HI THERE\"}},ROW_2=" +
    "{ID={TESTDATA=\"3\"},DATE={TESTDATA=\"2008-07-18\"},ITEM={TESTDATA=" +
    "\"Hello World\"}}}";

  /** String used as sample input or expected results data for conversions */
  private final String sampleTableStringCompressionTrue =
    "{ROW_1={ID=\"1\",DATE=\"2008-07-18\",ITEM=\"GOOD Morning\"}," +
     "ROW_2={ID=\"2\",DATE=\"2008-07-24\",ITEM=\"HI THERE\"}," +
     "ROW_3={ID=\"3\",DATE=\"2008-07-18\",ITEM=\"Hello World\"}}";

  /** String used as sample input or expected results data for conversions */
  private final String sampleTableStringWithCNameNullCompressionFalse =
    "{ROW_1={ID={CDATA=\"1\"},DATE={CDATA=\"2008-07-18\"},ITEM=" +
    "{CDATA=\"GOOD Morning\"}},ROW_2={ID={CDATA=\"2\"},DATE=" +
    "{CDATA=\"2008-07-24\"},ITEM={CDATA=\"HI THERE\"}},ROW_3=" +
    "{ID={CDATA=\"3\"},DATE={CDATA=\"2008-07-18\"},ITEM={CDATA=" +
    "\"Hello World\"}}}";

  /** String used as sample input or expected results data for conversions */
  private final String sampleXmlString =
    "<RESULTS><ROW><ID>1</ID><DATE>2008-07-18</DATE>" +
    "<ITEM>GOOD Morning</ITEM></ROW><ROW><ID>2</ID><DATE>" +
    "2008-07-24</DATE><ITEM>HI THERE</ITEM></ROW>" +
    "<ROW><ID>3</ID><DATE>2008-07-18</DATE><ITEM>" +
    "Hello World</ITEM></ROW></RESULTS>";
  private final String sampleXmlRootName = "RESULTS";  // Make sure this matches the root of the sample XML

  /** Default Xml2Tbl conversion options enum mask */
  private static final MaskValue<Xml2TblConversionOptions> DEF_XML2TBL_CONVERSION_OPTIONS = MaskValue.constant(Xml2TblConversionOptions.class);

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

  /** Creates a new instance of the class.
      @param methodName The name of the method being called.
      @param midas      The midas context testTo use when running the tests.
   */
  public XmlFileTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  //////////////////////////////////////////////////////////////////////////////

  /** Creates a new Table from a given String, runs it through fastXmlToTable,
      and compares the resulting String with a String containing the expected
      results.

      JWB - Since this method was made in response to Bug 1595, which dealt with
      a delim / cname issue, I am only making the initial test case for a NULL
      delim with a given cname. Other combinations can be added, if necessary.
   */
  public void testFastTableToXml () {
    Table table = new Table(sampleTableStringWithCNameCompressionFalse);

    String myXml = new String();
    myXml = XmlFile.fastTableToXml(table, "RESULTS", null, "TESTDATA", false, false);

    assertEquals("Test XmlFile.fastTableToXml", sampleXmlString, myXml);
  }

  /** Master test method for the various flavors of fastXmlToTbl
   */
  public void testFastXmlToTable () {
    fastXmlToTable1(); //fastXmlToTable(String, String, String, int, boolean)
    fastXmlToTable2(); //fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>)
    fastXmlToTable3(); //fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>)
                       //with IncludeRootName option set to true
  }

  /** Master test method for the various flavors of fastXmlToTbl
   */
  public void testFastXmlFileToTable () {
    // This first call checks to see if we have write permission the WRITE AUX
    // If not, warn and skip the test
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    if ( testXmlFile != null) {
        fastXmlFileToTable1(); //fastXmlFileToTable(Object ref, String url)
        fastXmlFileToTable2(); //fastXmlFileToTable(TextFile)
        fastXmlFileToTable3(); //fastXmlFileToTable(TextFile, String, String, int , boolean )
        fastXmlFileToTable4(); //fastXmlFileToTable(Object, String, MaskValue<Xml2TblConversionOptions>)
        fastXmlFileToTable5(); //fastXmlFileToTable(TextFile, MaskValue<Xml2TblConversionOptions>)
        fastXmlFileToTable6(); //fastXmlFileToTable(TextFile, String, String, int, MaskValue<Xml2TblConversionOptions>)
        fastXmlFileToTable7(); //fastXmlFileToTable where XML has '.' in key names

        testXmlFile.erase(true); // cleanup
    } else {
      Shell.warning("testFastXmlFileToTable: No write permission for temporary file. Skipping fastXmlFileToTable tests" );
    }
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(Object ref, String url)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
   */
  public void fastXmlFileToTable1 () {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringCompressionTrue);

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(getMidasContext(), testXmlFile.getURL());

    // Test
    assertEquals("Test XmlFile.fastXmlFileToTable(Object,String)", expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(TextFile)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
  */
  public void fastXmlFileToTable2() {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringCompressionTrue);

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(testXmlFile);

    // Test
    assertEquals("Test XmlFile.fastXmlFileToTable(TextFile)", expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(TextFile, String, String, int , boolean )

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
      @since NeXtMidas 3.3.3
  */
  public void fastXmlFileToTable3() {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringWithCNameCompressionFalse);

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(testXmlFile, null, "TESTDATA", 0, false);

    // Test
    assertEquals("Test fastXmlFileToTable(TextFile, String, String, int , boolean ) ",
                  expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(Object, String, MaskValue<Xml2TblConversionOptions>)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected results.
      @since NeXtMidas 3.3.3
  */
  public void fastXmlFileToTable4() {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringWithCNameNullCompressionFalse);
    // Default options Compression = false and IncludeRootName = false
    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(getMidasContext(),testXmlFile.getURL(), xml2TblconversionOptions);

    // Test
    assertEquals("Test fastXmlFileToTable(Object, String, MaskValue<Xml2TblConversionOptions>)",
                  expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(TextFile, MaskValue<Xml2TblConversionOptions>)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
      @since NeXtMidas 3.3.3
    */
  public void fastXmlFileToTable5() {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringWithCNameNullCompressionFalse);
    // Default options Compression = false and IncludeRootName = false
    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(testXmlFile, xml2TblconversionOptions);

    // Test
    assertEquals("Test fastXmlFileToTable(TextFile, String, MaskValue<Xml2TblConversionOptions>)",
                  expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlFileToTable(TextFile, String, String, int, MaskValue<Xml2TblConversionOptions>)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
      @since NeXtMidas 3.3.3
    */
  public void fastXmlFileToTable6() {
    // Setup Condition
    TextFile testXmlFile = createTestXmlFile(sampleXmlString);
    Table expTable = new Table(sampleTableStringWithCNameCompressionFalse);
    // Default options Compression = false and IncludeRootName = false
    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();

    // Execute
    Table resultsTable = XmlFile.fastXmlFileToTable(testXmlFile, null, "TESTDATA", 0, xml2TblconversionOptions);

    // Test
    assertEquals("Test fastXmlFileToTable(TextFile, String, String, int, MaskValue<Xml2TblConversionOptions>)",
                  expTable.toString(), resultsTable.toString());

    // Cleanup
    testXmlFile.erase(true);
  }

  /** Test Case for the following scenario in XmlFile
    fastXmlFileToTable where XML file has '.'s in the key names

    Reads the XML file, runs it through fastXmlToTable, and compares the
    resulting Table to a Table made from a String containing the expected
    results.
    @since NeXtMidas 4.0.1
  */
  public void fastXmlFileToTable7() {
    // Setup Condition

    TextFile testXmlFile = new TextFile(getMidas(), urlSysTest+"test_convert_dot_in_key.xml");
    String expectedTblStr = "{RELEASE.SERIES_0={VERSION.NUMBER=\"4.0.X\",TYPE.OF.RELEASE=\"stable series\"}"+
        ",RELEASE.SERIES_1={VERSION.NUMBER=\"4.1.X\",TYPE.OF.RELEASE=\"beta series\"}}";
    Table expSuggestedTable = new Table(expectedTblStr);

    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();

    // Execute
    Table actTable = XmlFile.fastXmlFileToTable(testXmlFile, null, "TESTDATA", 0, xml2TblconversionOptions);
    // Test
    assertEquals("Test fastXmlFileToTable with '.' in keys table",
        expSuggestedTable.toString(), actTable.toString());

    Object entry = actTable.get("RELEASE.SERIES_1");
    assertEquals("Test fastXmlFileToTable with '.' in keys entry",
        "{VERSION.NUMBER=\"4.1.X\",TYPE.OF.RELEASE=\"beta series\"}",entry.toString());
    assertEquals("Test fastXmlFileToTable with '.' in keys entry",
        "class nxm.sys.lib.Table",entry.getClass().toString());

  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlToTable(String, String, String, int, boolean)

      Creates a new XML String, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected results.

      JWB - Since this method was made in response to Bug 1595, which dealt with
      a delim / cname issue, I am only making the initial test case for a NULL
      delim with a given cname. Other combinations can be added, if necessary.
   */
  public void fastXmlToTable1() {
    Table  resultsTable = new Table();

    Table expTable = new Table(sampleTableStringWithCNameCompressionFalse);
    resultsTable = XmlFile.fastXmlToTable(sampleXmlString, null, "TESTDATA",0, false);

    assertEquals("Test fastXmlToTable(String, String, String, int, boolean)  ",
        expTable.toString(), resultsTable.toString());
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>)

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected
      results.
      @since NeXtMidas 3.3.3
    */
  public void fastXmlToTable2() {
    // Setup Condition
    Table expTable = new Table(sampleTableStringWithCNameCompressionFalse);
    // Default options Compression = false and IncludeRootName = false
    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();

    // Execute
    Table resultsTable = XmlFile.fastXmlToTable(sampleXmlString, null, "TESTDATA", 0, xml2TblconversionOptions);

    // Test
    assertEquals("Test fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>)",
                  expTable.toString(), resultsTable.toString());
  }

  /** Test Case for the following method in XmlFile
      public static Table fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>)
      with IncludeRootName option set to true

      Creates a new XML file, runs it through fastXmlToTable, and compares the
      resulting Table to a Table made from a String containing the expected results.
      @since NeXtMidas 3.3.3
    */
  public void fastXmlToTable3() {
    // Setup Condition
    Table expTable = new Table(sampleTableStringWithCNameCompressionFalse);
    // Default options Compression = false and IncludeRootName = false
    MaskValue<Xml2TblConversionOptions>  xml2TblconversionOptions =  DEF_XML2TBL_CONVERSION_OPTIONS.copy();
    xml2TblconversionOptions.set(Xml2TblConversionOptions.IncludeRootName);

    // Execute
    Table resultsTable = XmlFile.fastXmlToTable(sampleXmlString, null, "TESTDATA", 0, xml2TblconversionOptions);

    // Setup for test
    String rootName = resultsTable.getS(XmlFile.KEY_ROOTNAME);
    resultsTable.remove(XmlFile.KEY_ROOTNAME);

    // Test
    assertEquals("Test fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>) IncludeRootName=true",
                sampleXmlRootName,rootName);

    assertEquals("Test fastXmlToTable(String, String, String, int, MaskValue<Xml2TblConversionOptions>) IncludeRootName=true",
                  expTable.toString(), resultsTable.toString());
  }

  /** Create temporary file with sample xml
     @return TextFile. Returns null if file can not be created
   */
  private TextFile createTestXmlFile(String xmlString) {
    TextFile tf = null;
    try {
      String writeAuxPath = getMidasContext().io.getAuxPath(getMidasContext().io.getWriteAux());
      File path = new File(writeAuxPath);
      if (path.canWrite()) {
        File newTextFile = File.createTempFile("xmlfiletest_", ".xml", path);
        FileWriter fileWriter = new FileWriter(newTextFile);
        fileWriter.write(xmlString);
        fileWriter.close();

        FileName fn = new FileName(newTextFile);
        tf = new TextFile(getMidasContext(), fn);
      }
    } catch (Exception e) {
      Shell.warning("Unable to create XML for test: "+ e.getMessage());
      return null;
    }
    return tf;
  }

  public void testFromXML() {
    MapKBT mkbt = XmlFile.fromXML(sampleXmlString + "<!--this is commented out-->");
    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"1",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(0)).get("ID"));
    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"GOOD Morning",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(0)).get("ITEM"));

    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"2",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(1)).get("ID"));
    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"HI THERE",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(1)).get("ITEM"));

    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"3",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(2)).get("ID"));
    assertEquals("The should hold the same data - mapkbt="+mkbt+ " |||| xmlstring ="+sampleXmlString,"Hello World",((MapKBT)((ArrayList<?>)((MapKBT)mkbt.get("RESULTS")).get("ROW")).get(2)).get("ITEM"));

    // Test with dots in keys
    TextFile testXmlFile = new TextFile(urlSysTest+"test_convert_dot_in_key.xml");
    testXmlFile.open();
    String xmlString = testXmlFile.readAll();
    mkbt = XmlFile.fromXML(xmlString);
    Object entry = ((ArrayList<?>)((MapKBT)mkbt.get("XML")).get("RELEASE.SERIES")).get(1);
    assertEquals("MapKBT with '.' in Keys inner class is MapKBT", "class nxm.sys.inc.MapKBT$AbstractMapKBT", entry.getClass().toString());
    assertEquals("MapKBT with '.' in Keys values correct", "beta series", ((MapKBT)entry).get("TYPE.OF.RELEASE"));
    testXmlFile.close();


  }
    /**
     @Todo The following methods in XmlFile do not have corresponding test cases
     public static void parseXmlFast(String xml, SimpleXmlHandler handler)

     public static String fastTableToXml(Keyable tab, boolean newlines)
     public static String fastTableToXml(Keyable tab, String outerTag, String delim,
                                      String cname, boolean compress, boolean newlines)
     public static void fastTableToXmlFile(Object ref, String url, Keyable tab, boolean newlines)
     public static void fastTableToXmlFile(TextFile tf, Keyable tab, boolean newlines)
     public static void fastTableToXmlFile(TextFile tf, Keyable tab, String outerTag, String delim,
                                        String cname, boolean compress, boolean newlines)
   */
}
