/*
 * 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.Date;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.Locale;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;


/**
   Formats a {@link Date} in the format "HH:mm:ssdSSS", where the decimal
   separator, 'd', is locale specific.<br>
   For example, "15:49:37,459" for most European and East European countries,
   or "15:49:37.459" for most of the Americas, most of the Middle East and
   most of East Asia.

   @author Ceki G&uuml;lc&uuml;
   @author Andrew Vajoczki
   @author Mike McAngus

   @since 0.7.5
*/
public class AbsoluteTimeDateFormat extends DateFormat {

  /**
     String constant used to specify {@link
     org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current
     value is <b>ABSOLUTE</b>.
  */
  public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE";

  /**
     String constant used to specify {@link
     org.apache.log4j.helpers.DateTimeDateFormat} in layouts.  Current
     value is <b>DATE</b>.
  */
  public final static String DATE_AND_TIME_DATE_FORMAT = "DATE";

  /**
     String constant used to specify {@link
     org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current
     value is <b>ISO8601</b>.
  */
  public final static String ISO8601_DATE_FORMAT = "ISO8601";

  /*
     formatString specifies the String that would have been used if
     SimpleDateFormat were used to format the time instead of this
     class.  The decimal separator, and millisecond specification
     for the time will be added when this class is instantiated.
  */
  private String formatString = "HH:mm:ss";
  
  /*
     Set default decimal separator.  One or the other has to be chosen, so
     for historical purposes we choose the comma.
  */
  private char decimalSeparator = ',';

  public
  AbsoluteTimeDateFormat() {
  	this(TimeZone.getDefault(),Locale.getDefault());
  }

  public
  AbsoluteTimeDateFormat(TimeZone timeZone) {
  	this(timeZone,Locale.getDefault());
  }

  public
  AbsoluteTimeDateFormat(Locale locale) {
  	this(TimeZone.getDefault(),locale);
  }

  public
  AbsoluteTimeDateFormat(TimeZone timeZone, Locale locale) {
    setCalendar(Calendar.getInstance(timeZone, locale));
    setDecimalSeparator(locale);
  }

  private static long   previousTime;
  private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ssd"

  /**
     <p>
     Appends to <code>sbuf</code> the millisecond timestamp in <code>Locale</code>
     specific format.
     </p>
     <p>
     For example, "HH:mm:ss,SSS" (e.g. "15:49:37,459") for most European
     and East European countries, or "HH:mm:ss.SSS" (e.g. "15:49:37.459")
     for most of the Americas, most of the Middle East and most of East Asia.
     </p>

     @param date the date to format
     @param sbuf the string buffer to write to
     @param fieldPosition remains untouched
   */
  public
  StringBuffer format(Date date, StringBuffer sbuf,
		      FieldPosition fieldPosition) {

    long now = date.getTime();
    int millis = (int)(now % 1000);

    if ((now - millis) == previousTime) {
      sbuf.append(previousTimeWithoutMillis);
    }
    else {
      // We reach this point at most once per second
      // across all threads instead of each time format()
      // is called. This saves considerable CPU time.

      calendar.setTime(date);

      int start = sbuf.length();

      int hour = calendar.get(Calendar.HOUR_OF_DAY);
      if(hour < 10) {
	sbuf.append('0');
      }
      sbuf.append(hour);
      sbuf.append(':');

      int mins = calendar.get(Calendar.MINUTE);
      if(mins < 10) {
	sbuf.append('0');
      }
      sbuf.append(mins);
      sbuf.append(':');

      int secs = calendar.get(Calendar.SECOND);
      if(secs < 10) {
	sbuf.append('0');
      }
      sbuf.append(secs);
      sbuf.append(decimalSeparator);

      // store the time string for next time to avoid recomputation
      sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);

      previousTime = now - millis;
    }

    if(millis < 100) 
      sbuf.append('0');
    if(millis < 10) 
      sbuf.append('0');

    sbuf.append(millis);
    return sbuf;
  }

  /**
     Returns the format string that would be used by <code>SimpleDateFormat</code>
     if it was used to format the absolute time.
     <p>
     This method is protected because the format string is only of interest to the
     parse methods of subclasses.
     </p>

     @return The format <code>String</code>.
   */
  protected
  String getFormatString() {
    return formatString;
  }

  /**
     Sets the decimalSeparator used for displaying Millisecond timestamps.
   */
  private
  void setDecimalSeparator(Locale locale) {
  	NumberFormat nf = NumberFormat.getInstance(locale);
  	if (nf instanceof DecimalFormat) {
      decimalSeparator =
        ((DecimalFormat)nf).getDecimalFormatSymbols().getDecimalSeparator();
  	}

    // set the rest of the formatString
    formatString += decimalSeparator + "SSS";
  }

  /**
     This method does not do anything but return <code>null</code>.
   */
  public
  Date parse(String s, ParsePosition pos) {
    return null;
  }
}
