/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.builder.entity;

// Java Classes

// Library Classes

// Framework Classes
import java.util.Calendar;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import gov.va.med.fw.util.builder.AbstractBuilder;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.infra.ImpreciseDate;

/**
 * @author Martin Francisco
 */
public class ImpreciseDateBuilder
    extends AbstractBuilder
{
    /**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -6032144500184433040L;
	private static final String PATTERN = "([0-9]{4})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:[.]([0-9]{0,4})?)?)?)?)?)?)?([\\+\\-][0-9]{4})?";
    private static final int GROUP_YEAR = 1;
    private static final int GROUP_MONTH = 2;
    private static final int GROUP_DAY = 3;
    private static final int GROUP_HOUR = 4;
    private static final int GROUP_MINUTE = 5;
    private static final int GROUP_SECOND = 6;
    private static final int GROUP_FRACTIONAL_SECOND = 7;
    private static final int GROUP_TIMEZONE = 8;

    private boolean standardizeDate;
    
    /**
     * Default contructor.
     */
    public ImpreciseDateBuilder()
    {
        super();
    }

    public boolean isStandardizeDate() {
		return standardizeDate;
	}

	public void setStandardizeDate(boolean standardizeDate) {
		this.standardizeDate = standardizeDate;
	}

	public ImpreciseDate build(String value) throws BuilderException
    {
        return (value == null) ? null : this.parseString(value);
    }

    private int convertToInt(String string)
    {
        return (new Integer(string)).intValue();
    }

    protected ImpreciseDate parseString(String string) throws BuilderException
    {
        Matcher matcher = Pattern
            .compile(ImpreciseDateBuilder.PATTERN)
            .matcher(string);

        if(matcher.matches())
        {
            String year = matcher.group(ImpreciseDateBuilder.GROUP_YEAR);
            String month = matcher.group(ImpreciseDateBuilder.GROUP_MONTH);
            String day = matcher.group(ImpreciseDateBuilder.GROUP_DAY);
            String hour = matcher.group(ImpreciseDateBuilder.GROUP_HOUR);
            String minute = matcher.group(ImpreciseDateBuilder.GROUP_MINUTE);
            String second = matcher.group(ImpreciseDateBuilder.GROUP_SECOND);
            String fractionalSecond = matcher
                .group(ImpreciseDateBuilder.GROUP_FRACTIONAL_SECOND);
            String timeZone = matcher
                .group(ImpreciseDateBuilder.GROUP_TIMEZONE);

            Calendar calendar = Calendar.getInstance();
            calendar.setLenient(false);
            calendar.clear();

            if((year != null) && !"0000".equals(year))
            {
                calendar.set(Calendar.YEAR, this.convertToInt(year));
            }

            if((month != null) && !"00".equals(month))
            {
                calendar.set(Calendar.MONTH, this.convertToInt(month) - 1);
            }

            if((day != null) && !"00".equals(day))
            {
                calendar.set(Calendar.DAY_OF_MONTH, this.convertToInt(day));
            }

            if(hour != null)
            {
                calendar.set(Calendar.HOUR_OF_DAY, this.convertToInt(hour));
            }

            if(minute != null)
            {
                calendar.set(Calendar.MINUTE, this.convertToInt(minute));
            }

            if(second != null)
            {
                calendar.set(Calendar.SECOND, this.convertToInt(second));
            }

            if(fractionalSecond != null)
            {
                double factor;

                if(fractionalSecond.length() == 1)
                {
                    factor = 100;
                }
                else if(fractionalSecond.length() == 2)
                {
                    factor = 10;
                }
                else if(fractionalSecond.length() == 3)
                {
                    factor = 1;
                }
                else
                {
                    factor = 0.10; // divide by 10
                }

                calendar.set(Calendar.MILLISECOND, (int) Math.round(this
                    .convertToInt(fractionalSecond)
                    * factor));
            }
            
            //If standardized flag is set to true, then accept the timezone
            //The flag has been set to false because Vista does not support timezones. So ESR now stores 
            //the time without considering the timezone from the incoming message.  
            if(isStandardizeDate())
            {
	            if(timeZone != null)
	            {
	                calendar.setTimeZone(TimeZone.getTimeZone("GMT" + timeZone));
	            }
            }

            return new ImpreciseDate(calendar);
        }
        else
        {
            throw new BuilderException(
                "the string " + string + " is in an invalid imprecise date format");
        }
    }
}