/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included  with this distribution in
 * the LICENSE.txt file.
 */

package org.apache.log4j.helpers.newdatetime;

import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Date;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;

// Contributors: Arndt Schoenewald <arndt@ibm23093i821.mc.schoenewald.de>

/**
   Provides a localized {@link Date} String by prepending the date formatted
   as "yyyy-MM-dd" to the absolute time formatted according to the description
   in {@link AbsoluteTimeDateFormat}.

   <p>Refer to the <a 
   href=http://www.cl.cam.ac.uk/~mgk25/iso-time.html>summary of the
   International Standard Date and Time Notation</a> for more
   information on this format.

   @author Ceki G&uuml;lc&uuml;
   @author Andrew Vajoczki
   @author Mike McAngus

   @since 0.7.5
*/
public class ISO8601DateFormat extends AbsoluteTimeDateFormat {

  /*
     If anything needs to parse a date that this class returns, then we will
     use a SimpleDateFormat.  The parser will be instantiated only if the
     parse method is called for this instance.
  */
  private SimpleDateFormat parser;
    
  private static final String[] MONTHS = {"-01-","-02-","-03-",
                                          "-04-","-05-","-06-",
                                          "-07-","-08-","-09-",
                                          "-10-","-11-","-12-"};

  public
  ISO8601DateFormat() {
    this(TimeZone.getDefault(), Locale.getDefault());
  }

  public
  ISO8601DateFormat(TimeZone timeZone) {
    this(timeZone, Locale.getDefault());
  }

  public
  ISO8601DateFormat(Locale locale) {
    this(TimeZone.getDefault(), locale);
  }

  public
  ISO8601DateFormat(TimeZone timeZone, Locale locale) {
    super(timeZone, locale);
    now = (Calendar)calendar.clone();
  }

  private static Calendar now;
  private static int      lastDate = 0;
  private static char[]   lastDateString = new char[11]; // "yyyy-MM-dd "

  /**
     Appends to <code>sbuf</code> the date and time in
     "yyyy-MM-dd HH:mm:ssdSSS" format, where "d" is the 
     locale specific decimal separator.
     For example, "1994-Nov-06 15:49:37,459" for most
     European and East European countries, or 
     "1994-Nov-06 15:49:37.459" for most of the Americas,
     most of the Middle East and most of East Asia.

     @param sbuf the string buffer to write to
     @see AbsoluteTimeDateFormat
  */
  public
  StringBuffer format(Date date, StringBuffer sbuf,
		      FieldPosition fieldPosition) {

    now.setTime(date);

    // Created comparison date in yyyyDDD format
    int today = (1000 * now.get(Calendar.YEAR)) +
                now.get(Calendar.DAY_OF_YEAR);

    if (today == lastDate) {
      sbuf.append(lastDateString);
    }
    else {
      // We reach this point at most once per day
      // across all threads instead of each time format()
      // is called. This saves considerable CPU time.

      lastDate = today;

      int start = sbuf.length();

      int year =  now.get(Calendar.YEAR);
      sbuf.append(year);

      sbuf.append(MONTHS[now.get(Calendar.MONTH)]);

      int day = now.get(Calendar.DAY_OF_MONTH);
      if(day < 10)
	sbuf.append('0');
      sbuf.append(day);
      sbuf.append(' ');

      sbuf.getChars(start, sbuf.length(), lastDateString, 0);
    }

    // Add the time to the date string.
    return super.format(date, sbuf, fieldPosition);
  }

  /**
     Returns a <code>Date</code> for the supplied date/time (<code>text</code>) string
     starting at the given parse position.

     <p>The <code>text</code> string <strong>MUST</strong> be in "yyyy-MM-dd HH:mm:ssdSSS"
     format, where "d" is the locale specific decimal separator.</p>

     @param text  The date/time string to be parsed

     @param pos   On input, the position at which to start parsing; on
                  output, the position at which parsing terminated, or the
                  start position if the parse failed.

     @return      A Date, or null if <code>text</code> could not be parsed.
   */
    public
    Date parse(String text, ParsePosition pos) {
      if (parser == null) {
          parser = new SimpleDateFormat("yyyy-MM-dd " + super.getFormatString());
          parser.setLenient(false);
      }
  
      return parser.parse(text,pos);
    }
}
