//
//  NSDate+FormatterAdditions.m
//  JobTalk
//
//  Created by Xummer on 14-5-16.
//  Copyright (c) 2014年 BST. All rights reserved.
//

#import "NSDate+FormatterAdditions.h"
#include <time.h>
#include <xlocale.h>

#define JTLocalizedString(key) [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]

#define ISO8601_MAX_LEN 25

@implementation NSDate (FormatterAdditions)

+ (NSDate *)dateFromISO8601String:(NSString *)iso8601 {
	if (!iso8601) {
        return nil;
    }
	
    const char *str = [iso8601 cStringUsingEncoding:NSUTF8StringEncoding];
    char newStr[ISO8601_MAX_LEN];
    bzero(newStr, ISO8601_MAX_LEN);
	
    size_t len = strlen(str);
    if (len == 0) {
        return nil;
    }
	
    // UTC dates ending with Z
    if (len == 20 && str[len - 1] == 'Z') {
        memcpy(newStr, str, len - 1);
        strncpy(newStr + len - 1, "+0000\0", 6);
    }
	
    // Timezone includes a semicolon (not supported by strptime)
    else if (len == 25 && str[22] == ':') {
        memcpy(newStr, str, 22);
        memcpy(newStr + 22, str + 23, 2);
    }
	
    // Fallback: date was already well-formatted OR any other case (bad-formatted)
    else {
        memcpy(newStr, str, len > ISO8601_MAX_LEN - 1 ? ISO8601_MAX_LEN - 1 : len);
    }
	
    // Add null terminator
    newStr[sizeof(newStr) - 1] = 0;
    
    struct tm tm = {
        .tm_sec = 0,
        .tm_min = 0,
        .tm_hour = 0,
        .tm_mday = 0,
        .tm_mon = 0,
        .tm_year = 0,
        .tm_wday = 0,
        .tm_yday = 0,
        .tm_isdst = -1,
    };
	
    if (strptime_l(newStr, "%FT%T%z", &tm, NULL) == NULL) {
        return nil;
    }
    
    return [NSDate dateWithTimeIntervalSince1970:mktime(&tm)];
}


- (NSString *)ISO8601String {
	struct tm *timeinfo;
	char buffer[80];
	
	time_t rawtime = (time_t)[self timeIntervalSince1970];
	timeinfo = gmtime(&rawtime);
	
	strftime(buffer, 80, "%Y-%m-%dT%H:%M:%SZ", timeinfo);
	
	return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}

+ (NSString *)stringWithFormatter:(NSString *)dateFormatter andDate:(NSDate *)date {
    if ([dateFormatter length] == 0) {
        return nil;
    }
    
    struct tm *timeinfo;
	char buffer[80];
	
	time_t rawtime = (time_t)[date timeIntervalSince1970];
	timeinfo = gmtime(&rawtime);
	
	strftime(buffer, 80, [dateFormatter UTF8String], timeinfo);
	
	return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}

- (NSString *)stringWithFormatter:(NSString *)dateFormatter {
    
    if ([dateFormatter length] == 0) {
        return nil;
    }
    
    // Change to Local time zone
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate:self];
    NSDate *localDate = [self dateByAddingTimeInterval: interval];
    return [[self class] stringWithFormatter:dateFormatter andDate:localDate];
}

- (NSString *)chatAttachmentFormatterString {
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate:self];
    NSDate *localDate = [self dateByAddingTimeInterval: interval];
    
    NSString *dateFormatter = @"%m/%d/%Y %H:%M:%S";
    
    return [[self class] stringWithFormatter:dateFormatter andDate:localDate];
}

- (NSString *)YMDHMSFormatterString {
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate:self];
    NSDate *localDate = [self dateByAddingTimeInterval: interval];
    
    NSString *dateFormatter = @"%Y%m%d%H%M%S";
    
    return [[self class] stringWithFormatter:dateFormatter andDate:localDate];
}

- (NSString *)chatMsgFormatterString {
    
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate:self];
    NSDate *localDate = [self dateByAddingTimeInterval: interval];
    
    NSDate *tmpDate = [NSDate date];
    interval = [zone secondsFromGMTForDate:tmpDate];
    NSDate *localCurDate = [tmpDate dateByAddingTimeInterval: interval];
    
    NSString *dateFormatter = @"%Y-%m-%d-%H-%M-%S";
    
    NSString *dateStr = [[self class] stringWithFormatter:dateFormatter andDate:localDate];
    NSString *currentDateStr = [[self class] stringWithFormatter:dateFormatter andDate:localCurDate];
    
    NSArray *dateComponents = [dateStr componentsSeparatedByString:@"-"];
    NSArray *currentDateComponents = [currentDateStr componentsSeparatedByString:@"-"];
    
    // Year
    if (![dateComponents[ 0 ] isEqualToString:currentDateComponents[ 0 ]]) {
        dateFormatter = @"%Y-%m";
    }
    // Month
    else if (![dateComponents[ 1 ] isEqualToString:currentDateComponents[ 1 ]]) {
        dateFormatter = @"%m-%d";
    }
    // Day
    else if (![dateComponents[ 2 ] isEqualToString:currentDateComponents[ 2 ]]) {
        dateFormatter = @"%m-%d";
    }
    // Min
    else if (![dateComponents[ 3 ] isEqualToString:currentDateComponents[ 3 ]]) {
        dateFormatter = @"%H:%M";
    }
    // Sec
    else {
        dateFormatter = @"%H:%M";
    }
    
    return [[self class] stringWithFormatter:dateFormatter andDate:localDate];
}

- (NSString *)briefTimeInWords {
	NSTimeInterval seconds = fabs([self timeIntervalSinceNow]);
	
	static NSNumberFormatter *numberFormatter = nil;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		numberFormatter = [[NSNumberFormatter alloc] init];
		numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
		numberFormatter.currencySymbol = @"";
		numberFormatter.maximumFractionDigits = 0;
	});
	
	// Seconds
	if (seconds < 60.0) {
		if (seconds < 2.0) {
			return JTLocalizedString(@"1s");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%ds"), (NSInteger)seconds];
	}
	
	NSTimeInterval minutes = round(seconds / 60.0);
	
	// Minutes
	if (minutes >= 0.0 && minutes < 60.0) {
		if (minutes < 2.0) {
			return JTLocalizedString(@"1m");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%dm"), (NSInteger)minutes];
	}
	
	// Hours
	else if (minutes >= 60.0 && minutes < 1440.0) {
		NSInteger hours = (NSInteger)round(minutes / 60.0);
		if (hours < 2) {
			return JTLocalizedString(@"1h");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%dh"), hours];
	}
	
	// Days
	else if (minutes >= 1440.0 && minutes < 525600.0) {
		NSInteger days = (NSInteger)round(minutes / 1440.0);
		if (days < 2) {
			return JTLocalizedString(@"1d");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%@d"),
				[numberFormatter stringFromNumber:[NSNumber numberWithInteger:days]]];
	}
	
	// Years
	else if (minutes >= 525600.0) {
		NSInteger years = (NSInteger)round(minutes / 525600.0);
		if (years < 2) {
			return JTLocalizedString(@"1y");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%@y"),
				[numberFormatter stringFromNumber:[NSNumber numberWithInteger:years]]];
	}
	
	return nil;
}

- (NSString *)localYMDString {
    return [self stringWithFormatter:@"%Y-%m-%d"];
}


- (NSString *)briefTimeForTimeLine {
	NSTimeInterval seconds = fabs([self timeIntervalSinceNow]);
	
	static NSNumberFormatter *numberFormatter = nil;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		numberFormatter = [[NSNumberFormatter alloc] init];
		numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
		numberFormatter.currencySymbol = @"";
		numberFormatter.maximumFractionDigits = 0;
	});
    
	NSTimeInterval minutes = round(seconds / 60.0);
	
	// Minutes
	if (minutes >= 0.0 && minutes < 60.0) {
		if (minutes < 2.0) {
			return JTLocalizedString(@"1m");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%dm"), (NSInteger)minutes];
	}
	
	// Hours
	else if (minutes >= 60.0 && minutes < 1440.0) {
		NSInteger hours = (NSInteger)round(minutes / 60.0);
		if (hours < 2) {
			return JTLocalizedString(@"1h");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%dh"), hours];
	}
	
	// in 7 Days
	else if (minutes >= 1440.0 && minutes < 10080.0) {
		NSInteger days = (NSInteger)round(minutes / 1440.0);
		if (days < 2) {
			return JTLocalizedString(@"1d");
		}
		return [NSString stringWithFormat:JTLocalizedString(@"%@d"),
				[numberFormatter stringFromNumber:[NSNumber numberWithInteger:days]]];
	}
	
	// > 7 Days
	else if (minutes >= 10080.0) {
        return [self stringWithFormatter:@"%m/%d/%Y"];
	}
	
	return nil;
}

- (NSString *)httpParameterString {
    return [self stringWithFormatter:@"%Y-%m-%d %H:%M:%S"];
}

- (NSString *)mobileIDDateString {
    return [self stringWithFormatter:@"%y%m%d%H%M%S000"];
}

- (NSDate *)endOfTheDay {
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
    NSUInteger preservedComponents = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit);
    NSDateComponents *comp = [calendar components:preservedComponents fromDate:self];
    [comp setMinute:59];
    [comp setHour:23];
    
    return [calendar dateFromComponents:comp];
}


+ (NSString *)timeInWordsFromTimeInterval:(NSTimeInterval)intervalInSeconds includingSeconds:(BOOL)includeSeconds {
	NSTimeInterval intervalInMinutes = round(intervalInSeconds / 60.0f);
	
	if (intervalInMinutes >= 0 && intervalInMinutes <= 1) {
		if (!includeSeconds) {
			return intervalInMinutes <= 0 ? JTLocalizedString(@"less than a minute") : JTLocalizedString(@"1 minute");
		}
		if (intervalInSeconds >= 0 && intervalInSeconds < 5) {
			return [NSString stringWithFormat:JTLocalizedString(@"less than %d seconds"), 5];
		} else if (intervalInSeconds >= 5 && intervalInSeconds < 10) {
			return [NSString stringWithFormat:JTLocalizedString(@"less than %d seconds"), 10];
		} else if (intervalInSeconds >= 10 && intervalInSeconds < 20) {
			return [NSString stringWithFormat:@"%d seconds", 20];
		} else if (intervalInSeconds >= 20 && intervalInSeconds < 40) {
			return JTLocalizedString(@"half a minute");
		} else if (intervalInSeconds >= 40 && intervalInSeconds < 60) {
			return JTLocalizedString(@"less than a minute");
	 	} else {
			return JTLocalizedString(@"1 minute");
		}
	} else if (intervalInMinutes >= 2 && intervalInMinutes <= 44) {
		return [NSString stringWithFormat:JTLocalizedString(@"%d minutes"), (NSInteger)intervalInMinutes];
	} else if (intervalInMinutes >= 45 && intervalInMinutes <= 89) {
		return JTLocalizedString(@"about 1 hour");
	} else if (intervalInMinutes >= 90 && intervalInMinutes <= 1439) {
		return [NSString stringWithFormat:JTLocalizedString(@"about %d hours"), (NSInteger)round(intervalInMinutes / 60.0f)];
	} else if (intervalInMinutes >= 1440 && intervalInMinutes <= 2879) {
		return JTLocalizedString(@"1 day");
	} else if (intervalInMinutes >= 2880 && intervalInMinutes <= 43199) {
		return [NSString stringWithFormat:JTLocalizedString(@"%d days"), (NSInteger)round(intervalInMinutes / 1440.0f)];
	} else if (intervalInMinutes >= 43200 && intervalInMinutes <= 86399) {
		return JTLocalizedString(@"about 1 month");
	} else if (intervalInMinutes >= 86400 && intervalInMinutes <= 525599) {
		return [NSString stringWithFormat:JTLocalizedString(@"%d months"), (NSInteger)round(intervalInMinutes / 43200.0f)];
	} else if (intervalInMinutes >= 525600 && intervalInMinutes <= 1051199) {
		return JTLocalizedString(@"about 1 year");
	} else {
		return [NSString stringWithFormat:JTLocalizedString(@"over %d years"), (NSInteger)round(intervalInMinutes / 525600.0f)];
	}
	return nil;
}


- (NSString *)timeInWords {
	return [self timeInWordsIncludingSeconds:YES];
}


- (NSString *)timeInWordsIncludingSeconds:(BOOL)includeSeconds {
	return [[self class] timeInWordsFromTimeInterval:fabs([self timeIntervalSinceNow]) includingSeconds:includeSeconds];
}

@end

@implementation NSDate (DateFormatterAdditions)

- (NSString *)timeStampStr {
    NSTimeInterval curTimeInterval = [self timeIntervalSince1970];
    
    NSString *result = [NSString stringWithFormat:@"%li", (unsigned long)curTimeInterval];
    
    while ([result length] < 13) {
        result = [result stringByAppendingString:@"0"];
    }
    
    return result;
}

+ (NSString *)curTimeStamp {
    return [[NSDate date] timeStampStr];
}

@end

@implementation NSNumber (DateFormatterAdditions)

- (NSDate *)dateValue {
    if (self) {
        return [NSDate dateWithTimeIntervalSince1970:[self integerValue]];
    }
    else {
        return nil;
    }
}

- (NSDate *)overZeroDateValue {
    if (self) {
        NSInteger iSelf = [self integerValue];
        if (iSelf > 0) {
            return [NSDate dateWithTimeIntervalSince1970:iSelf];
        }
        else {
            return nil;
        }
    }
    else {
        return nil;
    }
}

@end

@implementation NSString (DateFormatterAdditions)

- (NSTimeInterval)timeStamp {
    if (self.length > 10) {
        return [[self substringToIndex:10] integerValue];
    }
    else {
        return [self integerValue];
    }
}

- (NSDate *)dateValue {
    if (self) {
        return [NSDate dateWithTimeIntervalSince1970:[self timeStamp]];
    }
    else {
        return nil;
    }
}

- (NSDate *)overZeroDateValue {
    if (self) {
        NSInteger iSelf = [self timeStamp];
        if (iSelf > 0) {
            return [NSDate dateWithTimeIntervalSince1970:iSelf];
        }
        else {
            return nil;
        }
    }
    else {
        return nil;
    }
}

@end
