/*
 *******************************************************************************
 * Copyright (C) 2007-2013, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.impl.duration;

import java.text.FieldPosition;
import java.util.Date;

import com.ibm.icu.text.DurationFormat;
import com.ibm.icu.util.ULocale;

/**
 * @author srl
 */
public class BasicDurationFormat extends DurationFormat {
    
    /**
     * 
     */
    private static final long serialVersionUID = -3146984141909457700L;
    
    transient DurationFormatter formatter;
    transient PeriodFormatter pformatter;
    transient PeriodFormatterService pfs = null;

    public static BasicDurationFormat getInstance(ULocale locale) {
        return new BasicDurationFormat(locale);
    }

    public StringBuffer format(Object object, StringBuffer toAppend, FieldPosition pos) {
        if(object instanceof Long) {
            String res = formatDurationFromNow(((Long)object).longValue());
            return toAppend.append(res);
        } else if(object instanceof Date) {
            String res = formatDurationFromNowTo(((Date)object));
            return toAppend.append(res);
        } else if (object instanceof javax.xml.datatype.Duration) {
            String res = formatDuration(object);
            return toAppend.append(res);
        }
        throw new IllegalArgumentException("Cannot format given Object as a Duration");

    }
    
    public BasicDurationFormat() {
        pfs = BasicPeriodFormatterService.getInstance();
        formatter = pfs.newDurationFormatterFactory().getFormatter();
        pformatter = pfs.newPeriodFormatterFactory().setDisplayPastFuture(false).getFormatter();
    }
    /**
     * 
     */
    public BasicDurationFormat(ULocale locale) {
        super(locale);
        pfs = BasicPeriodFormatterService.getInstance();
        formatter = pfs.newDurationFormatterFactory().setLocale(locale.getName()).getFormatter();
        pformatter = pfs.newPeriodFormatterFactory().setDisplayPastFuture(false).setLocale(locale.getName()).getFormatter();
    }

    /* (non-Javadoc)
     * @see com.ibm.icu.text.DurationFormat#formatDurationFrom(long, long)
     */
    public String formatDurationFrom(long duration, long referenceDate) {
        return formatter.formatDurationFrom(duration, referenceDate);
    }

    /* (non-Javadoc)
     * @see com.ibm.icu.text.DurationFormat#formatDurationFromNow(long)
     */
    public String formatDurationFromNow(long duration) {
        return formatter.formatDurationFromNow(duration);
    }

    /* (non-Javadoc)
     * @see com.ibm.icu.text.DurationFormat#formatDurationFromNowTo(java.util.Date)
     */
    public String formatDurationFromNowTo(Date targetDate) {
        return formatter.formatDurationFromNowTo(targetDate);
    }

    /** 
     *  JDK 1.5+ only
     * @param obj Object being passed.
     * @return The PeriodFormatter object formatted to the object passed.
     * @see "http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/datatype/Duration.html"
     */
    public String formatDuration(Object obj) {
        javax.xml.datatype.DatatypeConstants.Field inFields[] = { 
                javax.xml.datatype.DatatypeConstants.YEARS,
                javax.xml.datatype.DatatypeConstants.MONTHS,
                javax.xml.datatype.DatatypeConstants.DAYS,
                javax.xml.datatype.DatatypeConstants.HOURS,
                javax.xml.datatype.DatatypeConstants.MINUTES,
                javax.xml.datatype.DatatypeConstants.SECONDS,
            };
             TimeUnit outFields[] = { 
                TimeUnit.YEAR,
                TimeUnit.MONTH,
                TimeUnit.DAY,
                TimeUnit.HOUR,
                TimeUnit.MINUTE,
                TimeUnit.SECOND,
            };

        javax.xml.datatype.Duration inDuration = (javax.xml.datatype.Duration)obj;
        Period p = null;
        javax.xml.datatype.Duration duration = inDuration;
        boolean inPast = false;
        if(inDuration.getSign()<0) {
            duration = inDuration.negate();
            inPast = true;
        }
        // convert a Duration to a Period
        boolean sawNonZero = false; // did we have a set, non-zero field?
        for(int i=0;i<inFields.length;i++) {
            if(duration.isSet(inFields[i])) {
                Number n = duration.getField(inFields[i]);
                if(n.intValue() == 0 && !sawNonZero) {
                    continue; // ignore zero fields larger than the largest nonzero field
                } else {
                    sawNonZero = true;
                }
                float floatVal = n.floatValue();
                // is there a 'secondary' unit to set?
                TimeUnit alternateUnit = null;
                float alternateVal = 0;
                
                // see if there is a fractional part
                if(outFields[i]==TimeUnit.SECOND) {
                    double fullSeconds = floatVal;
                    double intSeconds = Math.floor(floatVal);
                    double millis = (fullSeconds-intSeconds)*1000.0;
                    if(millis > 0.0) {
                        alternateUnit = TimeUnit.MILLISECOND;
                        alternateVal=(float)millis;
                        floatVal=(float)intSeconds;
                    }
                }
                
                if(p == null) {
                    p = Period.at(floatVal, outFields[i]);
                } else {
                    p = p.and(floatVal, outFields[i]);
                }
                
                if(alternateUnit != null) {
                    p = p.and(alternateVal, alternateUnit); // add in MILLISECONDs
                }
            }
        }
        
        if(p == null) {
            // no fields set  = 0 seconds
            return formatDurationFromNow(0);
        } else {
            if(inPast) {// was negated, above.
                p = p.inPast();
            } else {
                p = p.inFuture();
            }
        }
        
        // now, format it.
        return pformatter.format(p);
    }
}
