package hirondelle.web4j.util;

import junit.framework.*;
//import junit.ui.TestRunner;
//import junit.textui.TestRunner;
import java.util.logging.*;
import java.util.*;
import java.math.BigDecimal;
import hirondelle.web4j.util.Util;

/**
 <a href="http://www.junit.org">JUnit</a> test cases for {@link Util}. 
*/
public final class TESTUtil extends TestCase {

  /**
   Run the test cases.
  */
   public static void main(String args[]) {
     String[] testCaseName = {TESTUtil.class.getName()};
     //Select one of several types of interfaces.
     junit.textui.TestRunner.main(testCaseName);
     //junit.swingui.TestRunner.main(testCaseName);
     //junit.ui.TestRunner.main(testCaseName);
  }
  
  public TESTUtil(String aName) {
   super(aName);
  }
  
  // TEST CASES //
  
  public void testReplace(){
    testReplaceError(null, null, null);
    testReplaceError(null, SUBSTR, REPLACEMENT);
    testReplaceError(INPUT, null, REPLACEMENT);
    testReplaceError(INPUT, SUBSTR, null);
    
    testReplaceSuccess(INPUT, SUBSTR, REPLACEMENT, RESULT);
    testReplaceSuccess("this is silly this is silly", SUBSTR, REPLACEMENT, "this *IS* silly this *IS* silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this *IS*  silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this  *IS* silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this  *IS*  silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this *IS* silly ");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, " this *IS* silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this *is* silly");
    testReplaceFailure(INPUT, SUBSTR, REPLACEMENT, "this IS silly");
  }
  
  public void testLogOnePerLine(){
    List list = new ArrayList();
    list.add(null); 
    list.add("Zebra"); 
    list.add("aardvark");
    list.add("Banana");
    list.add("");
    list.add("aardvark");
    list.add(new BigDecimal("5.00"));
    String listResult =  "(7) {" + NL + 
    "  ''" + NL +
    "  '5.00'" + NL +
    "  'aardvark'" + NL +
    "  'aardvark'" + NL +
    "  'Banana'" + NL +
    "  'null'" + NL + 
    "  'Zebra'" + NL + 
    "}";
    testLogOnePerLineSucceed(list, listResult);
    list = new ArrayList();
    listResult =  "(0) {" + NL + 
    "}";
    testLogOnePerLineSucceed(list, listResult);
    
    Map map = new LinkedHashMap();
    map.put("b", "blah");
    map.put("a", new BigDecimal("5.00"));
    map.put("Z", null);
    map.put(null, new Integer(3));
    String mapResult =  "(4) {" + NL + 
    "  'a' = '5.00'" + NL +
    "  'b' = 'blah'" + NL + 
    "  'null' = '3'" + NL + 
    "  'Z' = 'null'" + NL + 
    "}";
    testLogOnePerLineSucceed(map, mapResult);
    
    map = new LinkedHashMap();
    mapResult =  "(0) {" + NL + 
    "}";
    testLogOnePerLineSucceed(map, mapResult);
  }
  
  public void testRemoveQuotes(){
    assertTrue(Util.removeQuotes("Yes").equals("Yes"));
    assertTrue(Util.removeQuotes("'Yes'").equals("Yes")); 
    assertTrue(Util.removeQuotes("'Yes").equals("Yes"));
    assertTrue(Util.removeQuotes("'Yes").equals("Yes"));
    assertTrue(Util.removeQuotes("Yes'").equals("Yes"));
    assertTrue(Util.removeQuotes("\"Yes\"").equals("Yes"));
    assertTrue(Util.removeQuotes("\"Yes").equals("Yes"));
    assertTrue(Util.removeQuotes("Yes\"").equals("Yes"));
    
    assertTrue(Util.removeQuotes(" Yes").equals(" Yes"));
    assertTrue(Util.removeQuotes(" 'Yes").equals(" 'Yes"));
    assertTrue(Util.removeQuotes(" 'Yes'").equals(" 'Yes"));
  }
  
  public void testNumDecimals(){
    assertTrue(Util.hasMaxDecimals(new BigDecimal("0"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("0.0"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("0.00"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("1"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("100"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("1.0"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("1.12"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("100.1"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("100.23"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("-100"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("-1.0"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("-1.12"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("-100.1"), 2));
    assertTrue(Util.hasMaxDecimals(new BigDecimal("-100.23"), 2));
    
    assertFalse(Util.hasMaxDecimals(new BigDecimal("-100.234"), 2));
    assertFalse(Util.hasMaxDecimals(new BigDecimal("1.234"), 2));
    assertFalse(Util.hasMaxDecimals(new BigDecimal("1.2345"), 2));
    assertFalse(Util.hasMaxDecimals(new BigDecimal("0.000"), 2));
    assertFalse(Util.hasMaxDecimals(new BigDecimal("-0.000"), 2));
    assertFalse(Util.hasMaxDecimals(new BigDecimal("-0.0000"), 2));

    boolean hasThrownEx = false;
    try {
      Util.hasMaxDecimals(new BigDecimal("23.23"), 0);
    }
    catch (Throwable ex){
      hasThrownEx = true;
    }
    assertTrue(hasThrownEx);
  }
  
  public void testHasNumDecimals(){
    testHasNumDecimals("100", 0, PASS);
    testHasNumDecimals("999.82", 2, PASS);
    testHasNumDecimals("100.1", 1, PASS);
    testHasNumDecimals("-89", 0, PASS);
    testHasNumDecimals("-89.56321", 5, PASS);
    testHasNumDecimals("8.1234567890", 10, PASS); 
    
    testHasNumDecimals("-89.56321", 4, FAIL);
  }
  
  public void testWithNoSpaces(){
    testWithNoSpacesSuccess("Is Coder Key", "IsCoderKey");
  }
  
  public void testBuildTimeZone(){
    testTimeZoneSuccess("GMT");
    testTimeZoneSuccess("America/Halifax");
    testTimeZoneSuccess(" GMT ");
    testTimeZoneSuccess("PST");
    
    testTimeZoneFailure("Pacific");
    testTimeZoneFailure("");
    testTimeZoneFailure(null);
  }
  
  public void testInitialCapital(){
    testCap("blah", "Blah");
    testCap(" blah", " blah");
    testCap(" blah ", " blah ");
    testCap("Blah", "Blah");
    testCap("b", "B");
    testCap("b ", "B ");
    
    //failures
    testCapFails("blah", "blah");
    testCapFails("blah", "BLAH");
    testCapFails("blah", "Blah ");
    testCapFails("blah", " Blah");
    
    //testCapFails(" ", "B "); fails 
  }
  
  public void testQuote(){
    assertTrue(Util.quote("blah").equals("'blah'"));
    assertTrue(Util.quote("").equals("''"));
    assertTrue(Util.quote(null).equals("'null'"));
    assertTrue(Util.quote(" ").equals("' '"));
    
    char[] word = null;
    assertTrue(Util.quote(word).equals("'null'"));
    char[] word2  = {'a', 'b'};
    assertTrue(Util.quote(word2).startsWith("'[C@")); //arrays of primitives don't work well
    
    String thing = null;
    assertTrue(Util.quote(thing).equals("'null'"));
    
    List<String> names = Arrays.asList("blah", null);
    assertTrue(Util.quote(names).equals("'[blah, null]'"));
  }
  
  // FIXTURE //
  protected void setUp(){
  }

  /**
   Re-set test objects.
  */
  protected void tearDown() {
  }

  // PRIVATE  //
  private static final String INPUT = "this is silly";
  private static final String SUBSTR = " is ";
  private static final String REPLACEMENT = " *IS* ";
  private static final String RESULT = "this *IS* silly";
  private static final String NL = Consts.NEW_LINE;
  private static final boolean PASS = true;
  private static final boolean FAIL = false;
  
  private static final Logger fLogger = Util.getLogger(TESTUtil.class);
  
  /**
   Tests conditions which should generate an exception. 
  */
  private void testReplaceError(String aInput, String aOld, String aNew){
    boolean hasError = false;
    try {
      Util.replace(aInput, aOld, aNew);
    }
    catch (Throwable ex){
      hasError = true;
    }
    if ( ! hasError ){
      fail(
        "Input : " + Util.quote(aInput) + 
        " Old : " + Util.quote(aOld) + 
        " New : " + Util.quote(aNew) 
      );
    }
  }
  
  private void testReplaceSuccess(
    String aInput, String aSubstr, String aReplacement, String aExpectedResult
  ){
    String result = Util.replace(aInput, aSubstr, aReplacement);
    if ( ! result.equals(aExpectedResult) ) {
      fail(
        Util.quote(result) + " does not equal expected result : " + 
        Util.quote(aExpectedResult)
      );
    }
  }

  private void testReplaceFailure(
    String aInput, String aSubstr, String aReplacement, String aExpectedMismatch
  ){
    String result = Util.replace(aInput, aSubstr, aReplacement);
    if ( result.equals(aExpectedMismatch) ) {
      fail(
        Util.quote(result) + " equals, but expected mismatch : " + 
        Util.quote(aExpectedMismatch)
      );
    }
  }
  
  private void testLogOnePerLineSucceed(Collection aInput, String aExpectedMatch){
    String result = Util.logOnePerLine(aInput);
    if( ! result.equals(aExpectedMatch) ){
      fail(
        Util.quote(result) + " doesn't equal, but expected match : " + 
        Util.quote(aExpectedMatch)
      );
    }
  }

  private void testLogOnePerLineSucceed(Map aInput, String aExpectedMatch){
    String result = Util.logOnePerLine(aInput);
    if( ! result.equals(aExpectedMatch) ){
      fail(
        Util.quote(result) + " doesn't equal, but expected match : " + 
        Util.quote(aExpectedMatch)
      );
    }
  }
  
  private void testWithNoSpacesSuccess(String aInput, String aExpectedMatch){
    String result = Util.withNoSpaces(aInput);
    if( ! result.equals(aExpectedMatch) ){
      fail(
        Util.quote(result) + " doesn't equal, but expected match : " + 
        Util.quote(aExpectedMatch)
      );
    }
  }
  
  private void testTimeZoneSuccess(String aTimeZone){
    Util.buildTimeZone(aTimeZone);
  }
  
  private void testTimeZoneFailure(String aTimeZone){
    try {
      Util.buildTimeZone(aTimeZone);
      fail("Expected failure for time zone " + Util.quote(aTimeZone));
    }
    catch (Throwable ex){
      //expected failure
    }
  }
  
  private void testCap(String aInput, String aExpectedValue){
    String value = Util.withInitialCapital(aInput);
    assertTrue( value.equals(aExpectedValue));
  }
  
  private void testCapFails(String aInput, String aExpectedValue){
    String value = Util.withInitialCapital(aInput);
    assertFalse( value.equals(aExpectedValue));
  }
  
  private void testHasNumDecimals(String aAmount, int aNumDecimals, boolean aSucceed){
    BigDecimal amount = new BigDecimal(aAmount);
    if (aSucceed){
      assertTrue(Util.hasNumDecimals(amount, aNumDecimals));
    }
    else {
      assertFalse(Util.hasNumDecimals(amount, aNumDecimals));
    }
  }
  
  private static void log(Object aObject){
    System.out.println(String.valueOf(aObject));
  }
  
}
