/*
 * 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.DateFormatSymbols;
import java.text.SimpleDateFormat;

/**
   Provides a localized {@link Date} String by prepending the date formatted
   as "dd MMM yyyy" to the time formatted according to the description in
   {@link AbsoluteTimeDateFormat}.

   @author Ceki G&uuml;lc&uuml;
   @author Mike McAngus

   @since 0.7.5
*/
public class DateTimeDateFormat extends AbsoluteTimeDateFormat {

  String[] shortMonths;

  /*
     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;

  public
  DateTimeDateFormat() {
    this(TimeZone.getDefault(), Locale.getDefault());
  }

  public
  DateTimeDateFormat(TimeZone timeZone) {
    this(timeZone, Locale.getDefault());
  }

  public
  DateTimeDateFormat(Locale locale) {
    this(TimeZone.getDefault(),locale);
  }

  public
  DateTimeDateFormat(TimeZone timeZone, Locale locale) {
    super(timeZone, locale);
    shortMonths = new DateFormatSymbols(locale).getShortMonths();
    now = (Calendar)calendar.clone();
  }

  private Calendar now;
  private int      lastDate = 0;
  private char[]   lastDateString = new char[12]; // "dd MMM yyyy "

  /**
     Appends to <code>sbuf</code> the date and time in
     "dd MMM yyyy HH:mm:ssdSSS" format, where "d" is the
     locale specific decimal separator.
     For example, "06 Nov 1994 15:49:37,459" for most
     European and East European countries, or 
     "06 Nov 1994 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 day = now.get(Calendar.DAY_OF_MONTH);
      if(day < 10)
        sbuf.append('0');
      sbuf.append(day);
      sbuf.append(' ');
      sbuf.append(shortMonths[now.get(Calendar.MONTH)]);
      sbuf.append(' ');

      int year =  now.get(Calendar.YEAR);
      sbuf.append(year);
      sbuf.append(' ');

try {
      sbuf.getChars(start, sbuf.length(), lastDateString, 0);
} catch (ArrayIndexOutOfBoundsException e) {
	System.out.println("start          = " + start);
	System.out.println("sbuf.length    = " + sbuf.length());
	System.out.println("sbuf           = " + sbuf.toString());
	throw e;
}
    }

    // 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 "dd MMM yyyy 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("dd MMM yyyy " + super.getFormatString());
        parser.setLenient(false);
    }

    return parser.parse(text,pos);
  }
}
