package nxm.sys.test.junit.lib;

import nxm.sys.lib.*;
import nxm.sys.test.junit.NMTestCase;

/** This will test the Cache class. Only tests LRU at this time.
    @author  J. Czechowski IV
 */
public final class MFormatTest extends NMTestCase {
  /** public constructor to allow running this test case directly with JUnit. */
  public MFormatTest (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 MFormatTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  private void testBoth(String name, MFormat format, Object[] tests) {
    testFormat(name, format, tests);
    testParse( name, format, tests);
  }

  private void testFormat(String name, MFormat format, Object[] tests) {
    for (int i = 0; i < tests.length; i+=2) {
      String actual = format.format(tests[i]);
      assertEquals("Test "+name+": "+tests[i], tests[i+1], actual);
    }
  }

  private void testParse(String name, MFormat format, Object[] tests) {
    for (int i = 0; i < tests.length; i+=2) {
      String message = "Test "+name+": "+tests[i+1];

      try {
        Object actual = format.parseObject(tests[i+1].toString());
        if (tests[i+1] != null) {
          assertSimilar(message, tests[i], actual);
        } else {
          throw new RuntimeException(message+" expected ParseException, got "+actual);
        }
      } catch (java.text.ParseException e) {
        if (tests[i+1] != null) {
          throw new RuntimeException(message+" expected "+tests[i]+", got ParseException",e);
        } else {
          // Correct behavior
        }
      }
    }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  public void testToString()    { } // No test required... just informational
  public void testFormat()      { } // Tested in other methods
  public void testGetColor()    { } // Tested in other methods
  public void testParseObject() { } // Tested in other methods

  public void testConvertFortranPattern() {
// Jay: please add more tests for this. The syntax is:
//          "<fortran_format>", "<java_format>",
    final String[] TO_FORMAT = {
      "I4.2", "##00",
      "I4"  , "###0",   // test for Bug 2047
      "F4"  , "###0",   // test for Bug 2047
      "F4.1", "#0.0",   // test for Bug 2047
      "F4.2", "0.00",   // test for Bug 2047
      "F6.2", "##0.00", // test for Bug 2047
    };

    for (int i = 0; i < TO_FORMAT.length; i+=2) {
      String fortranPattern = TO_FORMAT[i];
      String actual = MFormat.convertFortranPattern(fortranPattern);
      assertEquals("Test convertFortranPattern: "+fortranPattern, TO_FORMAT[i+1], actual);
    }
  }

  public void testGetStringFormat() {
    final Object[] TESTS = {
      "ABC",                  "ABC",
      "123",                  "123",
      "01234567890123456789", "01234567890123456789",
      Integer.valueOf(4),     "4",
      Float.valueOf(1.0f),    "1.0"
    };
    testBoth("getStringFormat", MFormat.getStringFormat(), TESTS);
  }

  public void testGetAsciiFormat() {
    final Object[] TESTS = {
      "ABC",                  "ABC",
      "123",                  "123",
      Integer.valueOf(4),     "4",
      Float.valueOf(1.0f),    "1.0"
    };
    final Object[] TESTS_FORMAT = {
      "01234567890123456789", "0123456789012345", // <-- Trims string to 16 chars
    };
    final Object[] TESTS_PARSE = {
      "0123456789012345",     "0123456789012345", // <-- Can't add trimmed characters
    };

    testBoth  ("getAsciiFormat", MFormat.getAsciiFormat(16), TESTS);
    testFormat("getAsciiFormat", MFormat.getAsciiFormat(16), TESTS_FORMAT);
    testParse ("getAsciiFormat", MFormat.getAsciiFormat(16), TESTS_PARSE);
  }

  public void testGetByteFormat() {
    final Object[] TESTS = {
      Long.valueOf(0),            "0",
      Long.valueOf(1024),         "1k",
      Long.valueOf(4096),         "4k",
      Long.valueOf(16777216),     "16M",
      Long.valueOf(1073741824),   "1G"
    };
    testBoth("getByteFormat", MFormat.getByteFormat(), TESTS);
  }

  public void testGetDateFormatFor() {
    Time t = new Time(); t.fromCurrent();
    int year = t.getYear();
    String tcrExpected = Time.isLeapYear(year)?year+":06:03::21:49:37.503":year+":06:04::21:49:37.503";
   // String tcrExpected = Time.isLeapYear(year)?year+":06:03::21:49:37.503":year+":06:04::21:49:37.503";

    // Note: (1) Remember that times always round DOWN.
    //       (2) Times without dates convert start on 1970/01/01 when using Java parsers
    //       (3) 2007:06:04 is the 155th day of the year
    final String[] TEST_CASES_STANDARD = {
      // format                       oldDate                     string                           newDate
      "STD",                          "2007:06:04::21:49:37.503", "2007:06:04::21:49:37.503",      "2007:06:04::21:49:37.503",
      "ACQ",                          "2007:06:04::21:49:37.503", "07.155:21:49:37.503",           "2007:06:04::21:49:37.503",
      "EPOCH",                        "2007:06:04::21:49:37.503", "2007:13384177.503",             "2007:06:04::21:49:37.503",
      "NORAD",                        "2007:06:04::21:49:37.503", "07155.90946184",                "2007:06:04::21:49:37.503",
      "TCR",                          "2007:06:04::21:49:37.503", "155:21:49:37.503",              tcrExpected, // included leap year
      "VAX",                          "2007:06:04::21:49:37.503", "04-JUN-2007:21:49:37.503",      "2007:06:04::21:49:37.503",
      "HMS",                          "2007:06:04::21:49:37.503", "21:49:37.503",                  "21:49:37.503",
      "YMD",                          "2007:06:04::21:49:37.503", "2007:06:04",                    "2007:06:04::00:00:00",
    };
    final String[] TEST_CASES_CUSTOM = {
      "yyyy.MM.dd G 'at' HH:mm:ss z", "2007:06:04::21:49:37.503", "2007.06.04 AD at 21:49:37 UTC", "2007:06:04::21:49:37",
      "EEE, MMM d, yyyy",             "2007:06:04::21:49:37.503", "Mon, Jun 4, 2007",              "2007:06:04::00:00:00",
      "h:mm a",                       "2007:06:04::21:49:37.503", "9:49 PM",                       "21:49:00",
      "yyyy.MMMMM.dd GGG hh:mm aaa",  "2007:06:04::21:49:37.503", "2007.June.04 AD 09:49 PM",      "2007:06:04::21:49:00",
      "yyMMddHHmmssZ",                "2007:06:04::21:49:37.503", "070604214937+0000",             "2007:06:04::21:49:37",
      "yyyy-MM-dd'T'HH:mm:ss.SSSZ",   "2007:06:04::21:49:37.503", "2007-06-04T21:49:37.503+0000",  "2007:06:04::21:49:37.503"
    };

    testGetDateFormat(TEST_CASES_STANDARD, true);
    testGetDateFormat(TEST_CASES_CUSTOM,   true);
  }

  /** Only used bytestGetDateFormatFor(). */
  private void testGetDateFormat(String[] tests, boolean both) {
    double TOL = 0.01;

    for (int i = 0; i < tests.length; i+=4) {
      String  pattern = tests[i+0];
      String  oldTime = tests[i+1];
      String  string  = tests[i+2];
      String  newTime = tests[i+3];
      double  oldDate = Convert.o2d(oldTime);
      double  newDate = Convert.o2d(newTime);
      String  message = "Test getDateFormat pattern=\""+pattern+"\": given ";
      MFormat mformat = MFormat.getDateFormatFor(pattern);

      try {
        String actualFormat = mformat.format(Double.valueOf(oldDate));
        assertEquals(message+oldTime, string,  actualFormat);

        if (both) {
          double actualParse = Convert.o2d(mformat.parseObject(string));
          assertEquals(message+string, newDate, actualParse, TOL);
        }
      } catch (java.text.ParseException e) {
        throw new RuntimeException(message+" expected "+string+", got ParseException",e);
      }
    }
  }

  public void testGetAngleFormatFor() {
    final char   DEG      = nxm.sys.inc.UnicodeMap.DEGREE_SIGN;
    final Double ZERO     = Double.valueOf(0.0);
    final Double TOL_SEC0 = Double.valueOf(0.5 / 3600.0);      // Tolerance to 0.50000 sec
    final Double TOL_SEC3 = Double.valueOf(0.5 / 3600000.0);   // Tolerance to 0.00050 sec
    final Double EAST     = Double.valueOf(+123.75342933333333);
    final Double WEST     = Double.valueOf(-123.75342933333333);
    final Double NORTH    = Double.valueOf(+23.75342933333333);
    final Double SOUTH    = Double.valueOf(-23.75342933333333);

    final Object[] TEST_CASES_STANDARD = {
      // format                  oldAngle     string                     newAngle         tolerance
      "DMS",                     ZERO,          "00"+DEG+"00'00",        ZERO,            TOL_SEC0,
      "DMS",                     EAST,         "123"+DEG+"45'12",        EAST,            TOL_SEC0,
      "DMS",                     WEST,        "-123"+DEG+"45'12",        WEST,            TOL_SEC0,
      "DMS",                     NORTH,         "23"+DEG+"45'12",        NORTH,           TOL_SEC0,
      "DMS",                     SOUTH,        "-23"+DEG+"45'12",        SOUTH,           TOL_SEC0,

      "LON",                     ZERO,          "00"+DEG+"00'00E",       ZERO,            TOL_SEC0,
      "LON",                     EAST,         "123"+DEG+"45'12E",       EAST,            TOL_SEC0,
      "LON",                     WEST,         "123"+DEG+"45'12W",       WEST,            TOL_SEC0,

      "LAT",                     ZERO,          "00"+DEG+"00'00N",       ZERO,            TOL_SEC0,
      "LAT",                     NORTH,         "23"+DEG+"45'12N",       NORTH,           TOL_SEC0,
      "LAT",                     SOUTH,         "23"+DEG+"45'12S",       SOUTH,           TOL_SEC0,
    };
    final Object[] TEST_CASES_CUSTOM = {
      "+d.dddd",                 ZERO,         "+0.0000",          Double.valueOf(+0.0000),   ZERO,
      "+d.dddd",                 EAST,         "+123.7534",        Double.valueOf(+123.7534), ZERO,
      "+d.dddd",                 WEST,         "-123.7534",        Double.valueOf(-123.7534), ZERO,
      "+d.dddd",                 NORTH,        "+23.7534",         Double.valueOf(+23.7534),  ZERO,
      "+d.dddd",                 SOUTH,        "-23.7534",         Double.valueOf(-23.7534),  ZERO,

      "ddd:mm:ss.sssE",          ZERO,         "000:00:00.000E",         ZERO,            TOL_SEC3,
      "ddd:mm:ss.sssE",          EAST,         "123:45:12.346E",         EAST,            TOL_SEC3,
      "ddd:mm:ss.sssE",          WEST,         "123:45:12.346W",         WEST,            TOL_SEC3,

      "dd:mm:ss.sssN",           ZERO,         "00:00:00.000N",          ZERO,            TOL_SEC3,
      "dd:mm:ss.sssN",           NORTH,        "23:45:12.346N",          NORTH,           TOL_SEC3,
      "dd:mm:ss.sssN",           SOUTH,        "23:45:12.346S",          SOUTH,           TOL_SEC3,

      "+ddd'd' mm'm' ss.sss's'", ZERO,         "+000d 00m 00.000s",      ZERO,            TOL_SEC3,
      "+ddd'd' mm'm' ss.sss's'", EAST,         "+123d 45m 12.346s",      EAST,            TOL_SEC3,
      "+ddd'd' mm'm' ss.sss's'", WEST,         "-123d 45m 12.346s",      WEST,            TOL_SEC3,

      "+dd'd' mm'm' ss.sss's'",  ZERO,         "+00d 00m 00.000s",       ZERO,            TOL_SEC3,
      "+dd'd' mm'm' ss.sss's'",  NORTH,        "+23d 45m 12.346s",       NORTH,           TOL_SEC3,
      "+dd'd' mm'm' ss.sss's'",  SOUTH,        "-23d 45m 12.346s",       SOUTH,           TOL_SEC3,

      "dddmmssE",                ZERO,         "0000000E",               ZERO,            TOL_SEC0,
      "dddmmssE",                EAST,         "1234512E",               EAST,            TOL_SEC0,
      "dddmmssE",                WEST,         "1234512W",               WEST,            TOL_SEC0,

      "ddmmssN",                 ZERO,         "000000N",                ZERO,            TOL_SEC0,
      "ddmmssN",                 NORTH,        "234512N",                NORTH,           TOL_SEC0,
      "ddmmssN",                 SOUTH,        "234512S",                SOUTH,           TOL_SEC0,
    };

    testGetAngleFormatFor(TEST_CASES_STANDARD, false, true);
    testGetAngleFormatFor(TEST_CASES_CUSTOM,   true,  false);
  }

  /** Only used bytestGetDateFormatFor(). */
  private void testGetAngleFormatFor(Object[] tests, boolean both, boolean trim) {
    for (int i = 0; i < tests.length; i+=5) {
      String  pattern = (String)tests[i+0];
      Number  oldAng  = (Number)tests[i+1];
      String  string  = (String)tests[i+2];
      Number  newAng  = (Number)tests[i+3];
      Number  tol     = (Number)tests[i+4];
      String  message = "Test getAngleFormat pattern=\""+pattern+"\": given ";
      MFormat mformat = MFormat.getAngleFormatFor(pattern);

      try {
        String actualFormat = mformat.format(oldAng);
        if (trim) actualFormat = actualFormat.trim();
        assertEquals(message+oldAng, string, actualFormat);

        if (both) {
          double actualParse  = Convert.o2d(mformat.parseObject(string));
          assertEquals(message+string, newAng.doubleValue(), actualParse, tol.doubleValue());
        }
      }  catch (java.text.ParseException e) {
        throw new RuntimeException(message+" expected "+string+", got ParseException",e);
      }
    }
  }

// Need tests:
//  public void testGetNumberFormatFor() {
//
//  }
//
//  public void testGetEnumFormat() {
//
//  }

}
