package hirondelle.web4j.config;

import java.util.*;
import java.text.*;
import java.util.regex.*;

import hirondelle.web4j.request.DateConverter;

/**
* Implementation of {@link DateConverter}, required by WEB4J. 
*/
public final class DateConverterImpl implements DateConverter { 
  
  /**
  * Format a date for the human eye.
  *  
  * <P>Example return value, for January 1, 2006, 1:59am :<br> 
  * <tt>'01/31/2006 01:59'</tt>.
  * 
  * <P>Any trailing <tt>'00:00'</tt> values are removed.
  */
  public String formatEyeFriendly(Date aDate, Locale aLocale, TimeZone aTimeZone) {
    SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm"); //HH 00-23
    format.setTimeZone(aTimeZone);
    String result = format.format(aDate);
    //chop off any trailing 00:00
    String UNWANTED = "00:00";
    if ( result.endsWith(UNWANTED) ) {
      int end = result.length() - UNWANTED.length() - 1;
      result = result.substring(0,end);
    }
    return result;
  }
  
  /**
  * Parse a date entered in a hand-friendly style.
  *   
  *  <P>Example of the required input format, for January 1, 2006, 1:59am : <br>
  *  <tt>'01312006 01:59'</tt>.   
  *  
  *  <P>If no time portion is entered, then <tt>'00:00'</tt> is assumed.
  */
  public Date parseHandFriendly(String aInputValue, Locale aLocale, TimeZone aTimeZone) {
    //if no time portion, then assume 00:00
    return parse(aInputValue, HAND_FRIENDLY_REGEX, aTimeZone);
  }
  
  /**
  * Parse a date entered in an eye-friendly style.
  *   
  *  <P>Example of the required input format, for January 1, 2006, 1:59am : <br>
  *  <tt>'01/31/2006 01:59'</tt>
  *  
  *  <P>If no time portion is entered, then <tt>'00:00'</tt> is assumed.
  */
  public Date parseEyeFriendly(String aInputValue, Locale aLocale, TimeZone aTimeZone) {
    return parse(aInputValue, EYE_FRIENDLY_REGEX, aTimeZone);
  }
  
  // PRIVATE
  
  /*
  * Patterns are thread-safe. Matchers and SimpleDateFormats are NOT thread-safe. 
  * Items that are not thread-safe can be used only as local variables, not as fields.
  */

  /** Month in the Gregorian calendar: <tt>01..12</tt>.   */
  private static final String MONTH =
    "(01|02|03|04|05|06|07|08|09|10|11|12)"
  ;

  /** Day of the month in the Gregorian calendar: <tt>01..31</tt>.   */
  private static final String DAY_OF_MONTH = 
    "(01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)"
  ;
   
  /** Hours in the day <tt>00..23</tt>.  */
  private static final String HOURS = 
    "(00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23)"
  ;
   
  /** Minutes in an hour <tt>00..59</tt>.  */
  private static final String MINUTES = 
    "((0|1|2|3|4|5)\\d)"  
  ;

  /** Format : 01312006 01:59   */
  private static final Pattern HAND_FRIENDLY_REGEX = 
    Pattern.compile(MONTH + DAY_OF_MONTH + "(\\d\\d\\d\\d)" + "(?: " + HOURS + ":" + MINUTES +  ")?")
  ;
  
  /** Format : 01/31/2006 01:59   */
  private static final Pattern EYE_FRIENDLY_REGEX = 
    Pattern.compile(MONTH + "/" + DAY_OF_MONTH + "/" + "(\\d\\d\\d\\d)" + "(?: " + HOURS + ":" + MINUTES +  ")?")
  ;
  
  /**
  * Requires the month, day, year to be the first, second, and third groups, respectively. 
  * Optionally, hours and minutes can appear as 4th and 5th groups, respectively.
  */
  private Date parse(String aInputValue, Pattern aRegex, TimeZone aTimeZone){
    Date result = null;
    Matcher matcher = aRegex.matcher(aInputValue);
    if( matcher.matches() ) {
      Integer month = new Integer(matcher.group(1));
      Integer day = new Integer(matcher.group(2));
      Integer year = new Integer( matcher.group(3) );
      String hour = matcher.group(4);
      String minute = matcher.group(5);
      Calendar cal = null;
      if( hour == null ){
        cal = new GregorianCalendar(year.intValue(), month.intValue() - 1, day.intValue(), 0,0,0);
      }
      else {
        Integer hourVal = new Integer(hour);
        Integer minuteVal = new Integer(minute);
        cal = new GregorianCalendar(year.intValue(), month.intValue() - 1, day.intValue(), hourVal.intValue(), minuteVal.intValue(), 0);
      }
      cal.setTimeZone(aTimeZone);
      result = cal.getTime();
    }
    return result;
  }
}