#include "LCDateTools.h"
#include "GNUstep.h"

/**
* Provides support for converting dates to strings and vice-versa.
 * The strings are structured so that lexicographic sorting orders 
 * them by date, which makes them suitable for use as field values 
 * and search terms.
 * 
 * <P>This class also helps you to limit the resolution of your dates. Do not
 * save dates with a finer resolution than you really need, as then
 * RangeQuery and PrefixQuery will require more memory and become slower.
 * 
 * <P>Compared to {@link DateField} the strings generated by the methods
 * in this class take slightly more space, unless your selected resolution
 * is set to <code>Resolution.DAY</code> or lower.
 */
@implementation NSString (LuceneKit_Document_Date)

/** convert a date to string in this format depending on resolution:
 * "%Y%m%d%H%M%S%F" (yyyyMMddHHmmssSSS) in GMT.
 */
+ (id) stringWithCalendarDate: (NSCalendarDate *) date
                   resolution: (LCResolution) res;
{
	return [NSString stringWithTimeIntervalSince1970: [date timeIntervalSince1970]
										  resolution: res];
}

/** convert a millisecond to string in this format depending on resolution:
 * "%Y%m%d%H%M%S%F" (yyyyMMddHHmmssSSS) in GMT.
 */
+ (id) stringWithTimeIntervalSince1970: (NSTimeInterval) time
                            resolution: (LCResolution) resolution;
{
	NSTimeInterval interval;
	NSCalendarDate *date;
	NSString *pattern;
	date = [NSCalendarDate dateWithTimeIntervalSince1970: time];
	interval = [date timeIntervalSince1970WithResolution: resolution];
	date = [NSCalendarDate dateWithTimeIntervalSince1970: interval];
	/* Make sure date is in GMT format */
	[date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]];
	
	if (resolution == LCResolution_YEAR) {
		pattern = @"%Y";
	} else if (resolution == LCResolution_MONTH) {
		pattern = @"%Y%m";
	} else if (resolution == LCResolution_DAY) {
		pattern = @"%Y%m%d";
	} else if (resolution == LCResolution_HOUR) {
		pattern = @"%Y%m%d%H";
	} else if (resolution == LCResolution_MINUTE) {
		pattern = @"%Y%m%d%H%M";
	} else if (resolution == LCResolution_SECOND) {
		pattern = @"%Y%m%d%H%M%S";
	} else if (resolution == LCResolution_MILLISECOND) {
		pattern = @"%Y%m%d%H%M%S%F";
	} else {
		return nil; // Unknown Resolution
	}
	
	return [date descriptionWithCalendarFormat: pattern];
}

/**
* Converts a string produced by <code>timeToString</code> or
 * <code>dateToString</code> back to a time, represented as the
 * number of milliseconds since January 1, 1970, 00:00:00 GMT.
 * 
 * @param dateString the date string to be converted
 * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
 * @throws ParseException if <code>dateString</code> is not in the 
 *  expected format 
 */
- (NSTimeInterval) timeIntervalSince1970
{
	return [[self calendarDate] timeIntervalSince1970];
}

/* Convert a string in this format to date:
 * "%Y%m%d%H%M%S%F" (yyyyMMddHHmmssSSS) in GMT.
 */
- (NSCalendarDate *) calendarDate;
{
	NSString *pattern = nil;
	int len = [self length];
	switch(len)
	{
		case 4:
			pattern = @"%Y%Z";
			break;
		case 6:
			pattern = @"%Y%m%Z";
			break;
		case 8:
			pattern = @"%Y%m%d%Z";
			break;
		case 10:
			pattern = @"%Y%m%d%H%Z";
			break;
		case 12:
			pattern = @"%Y%m%d%H%M%Z";
			break;
		case 14:
			pattern = @"%Y%m%d%H%M%S%Z";
			break;
		case 17:
			pattern = @"%Y%m%d%H%M%S%F%Z";
			break;
		default: 
			return nil; // Not Valid Date String
	}
	
	/* make sure the string is in GMT format */
	return [NSCalendarDate dateWithString: [self stringByAppendingString:@"GMT"] 
                               calendarFormat: pattern];
}

@end

@implementation NSCalendarDate (LuceneKit_Document_Date)

/**
* Limit a date's resolution. For example, the date <code>1095767411000</code>
 * (which represents 2004-09-21 13:50:11) will be changed to 
 * <code>1093989600000</code> (2004-09-01 00:00:00) when using
 * <code>Resolution.MONTH</code>.
 * 
 * @param resolution The desired resolution of the date to be returned
 * @return the date with all values more precise than <code>resolution</code>
 *  set to 0 or 1, expressed as milliseconds since January 1, 1970, 00:00:00 GMT
 */
- (NSTimeInterval) timeIntervalSince1970WithResolution: (LCResolution) res
{
	return [[self dateWithResolution: res] timeIntervalSince1970];
}

/**
* Limit a date's resolution. For example, the date <code>2004-09-21 13:50:11</code>
 * will be changed to <code>2004-09-01 00:00:00</code> when using
 * <code>Resolution.MONTH</code>. 
 * 
 * @param resolution The desired resolution of the date to be returned
 * @return the date with all values more precise than <code>resolution</code>
 *  set to 0 or 1
 */
- (NSCalendarDate *) dateWithResolution: (LCResolution) res
{
	switch(res)
	{
		case LCResolution_YEAR:
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: 1
											day: 1
										   hour: 0
										 minute: 0
										 second: 0
									   timeZone: [self timeZone]];
		case LCResolution_MONTH:
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: [self monthOfYear]
											day: 1
										   hour: 0
										 minute: 0
										 second: 0
									   timeZone: [self timeZone]];
		case LCResolution_DAY:
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: [self monthOfYear]
											day: [self dayOfMonth]
										   hour: 0
										 minute: 0
										 second: 0
									   timeZone: [self timeZone]];
		case LCResolution_HOUR:
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: [self monthOfYear]
											day: [self dayOfMonth]
										   hour: [self hourOfDay]
										 minute: 0
										 second: 0
									   timeZone: [self timeZone]];
		case LCResolution_MINUTE:
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: [self monthOfYear]
											day: [self dayOfMonth]
										   hour: [self hourOfDay]
										 minute: [self minuteOfHour]
										 second: 0
									   timeZone: [self timeZone]];
		case LCResolution_SECOND: 
			return [NSCalendarDate dateWithYear: [self yearOfCommonEra]
										  month: [self monthOfYear]
											day: [self dayOfMonth]
										   hour: [self hourOfDay]
										 minute: [self minuteOfHour]
										 second: [self secondOfMinute]
									   timeZone: [self timeZone]];
		case LCResolution_MILLISECOND:
			return AUTORELEASE([self copy]);
			// don't cut off anything
		default:
			return nil; // Error;
	}
}

@end