package daruma.util;

import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.FieldPosition;

import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;


public class ISO8601DateFormat extends DateFormat
{
	private	static final long serialVersionUID = 0L;

	public	ISO8601DateFormat()
	{
	}


	@Override
	public	StringBuffer	format( Date  date ,
					StringBuffer  toAppendTo ,
					FieldPosition  fieldPosition )
	{
		if ( date == null
		  || toAppendTo == null
		  || fieldPosition == null )
		{
			return toAppendTo;
		}


		DateFormat	f = new java.text.SimpleDateFormat
					( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" );

		TimeZone tz	= TimeZone.getDefault();
		String	 id	= tz.getID();
		int	 offset = tz.getRawOffset();

		f.setTimeZone( new SimpleTimeZone( offset , id ) );


		String		s = f.format( date ,
					      toAppendTo ,
					      fieldPosition ).toString();

		/*
		 * タイムゾーン表記形式を RFC822(+0900) から
		 * ISO8601(+09:00) に変換
		 */
		if ( s.length() >= 5 )
		{
			char	sign    = s.charAt( s.length() - 5 );
			char	hour1   = s.charAt( s.length() - 4 );
			char	hour2   = s.charAt( s.length() - 3 );
			char	minute1 = s.charAt( s.length() - 2 );
			char	minute2 = s.charAt( s.length() - 1 );

			if ( (sign == '+' || sign == '-')
			     && Character.isDigit( hour1 )
			     && Character.isDigit( hour2 )
			     && Character.isDigit( minute1 )
			     && Character.isDigit( minute2 ) )

			return new StringBuffer
				( s.substring( 0 , s.length() - 2 )
				  + ':' + minute1 + minute2 );
		}

		return new StringBuffer( s );
	}


	@Override
	public	Date	parse( String  source ,  ParsePosition  pos )
	{
		DateFormat	baseFormat = new java.text.SimpleDateFormat
						 ( "yyyy-MM-dd'T'HH:mm:ss" );

		/* 後で時差は自分で計算するので、ここはUTCにする。 */
		baseFormat.setTimeZone( new SimpleTimeZone( 0 , "UTC" ) );

		Date	d = baseFormat.parse( source , pos );

		String	s = source;

		int	p = pos.getIndex();

		/*
		 * ISO8601 形式のタイムゾーンの読み取り。Java は
		 * ISO8601 形式(+09:00)のフォーマットをサポートしてい
		 * ない。RFC822 形式(+0900)をサポートしている。
		 */
		if ( p < s.length() )
		{
			if ( s.charAt( p ) == '.' )
			{
				p ++;

				long	milliSec = 0;
				int	factor = 100;

				for( ;  p < s.length()  ;  ++ p )
				{
					char	ch = s.charAt( p );
					if ( ! Character.isDigit( ch ) )
					{
						break;
					}

					int	n = Character.digit( ch , 10 );

					milliSec += (n * factor);

					factor /= 10;
				}

				d = new Date( d.getTime() + milliSec );
			}
		}
		else
		{
			return null;
		}


		if ( p >= s.length() )
		{
			return null;
		}

		char	ch = s.charAt( p );
		p ++;

		if ( ch == 'Z' )
		{
			// nothing to do
		}
		else
		{
			/* 時差の計算を行う */

			if ( s.length() - p < 5
			  || s.charAt( p + 2 ) != ':' )
			{
				return null;
			}

			long	sign;
			if ( ch == '+' )
			{
				sign = +1;
			}
			else if ( ch == '-' )
			{
				sign = -1;
			}
			else
			{
				return null;
			}


			char	hour1   = s.charAt( p );
			char	hour2   = s.charAt( p + 1 );
			char	minute1 = s.charAt( p + 3 );
			char	minute2 = s.charAt( p + 4 );

			if ( ! Character.isDigit( hour1 )
			  || ! Character.isDigit( hour2 )
			  || ! Character.isDigit( minute1 )
			  || ! Character.isDigit( minute2 ) )
			{
				return null;
			}

			long	hours   = Character.digit( hour1 , 10 ) * 10
					  + Character.digit( hour2 , 10 );

			long	minutes = Character.digit( minute1 , 10 ) * 10
					  + Character.digit( minute2 , 10 );

			d = new Date( d.getTime()
				      - sign * 1000 * 60 * minutes
				      - sign * 1000 * 60 * 60 * hours );

			p += 5;
		}

		pos.setIndex( p );

		return d;
	}


	private static ISO8601DateFormat instance = new ISO8601DateFormat();

	public static ISO8601DateFormat getISO8601Instance()
	{
		return instance;
	}
}
