#import "MGLCoordinateFormatter.h"

#import "NSBundle+MGLAdditions.h"
#import "NSValue+MGLAdditions.h"

@implementation MGLCoordinateFormatter

- (instancetype)init {
    if (self = [super init]) {
        _allowsMinutes = YES;
        _allowsSeconds = YES;
        _unitStyle = NSFormattingUnitStyleMedium;
    }
    return self;
}

- (NSString *)stringFromCoordinate:(CLLocationCoordinate2D)coordinate {
    NSString *positiveLatitudeFormat;
    NSString *negativeLatitudeFormat;
    NSString *positiveLongitudeFormat;
    NSString *negativeLongitudeFormat;
    NSString *stringFormat;
    switch (self.unitStyle) {
        case NSFormattingUnitStyleShort:
            positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_SHORT", @"Foundation", nil, @"%@N", @"North latitude format, short: {latitude}");
            negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_SHORT", @"Foundation", nil, @"%@S", @"South latitude format, short: {latitude}");
            positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_SHORT", @"Foundation", nil, @"%@E", @"East longitude format, short: {longitude}");
            negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_SHORT", @"Foundation", nil, @"%@W", @"West longitude format, short: {longitude}");
            stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_SHORT", @"Foundation", nil, @"%@, %@", @"Coordinate pair format, short: {latitude}, {longitude}");
            break;
            
        case NSFormattingUnitStyleMedium:
            positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_MEDIUM", @"Foundation", nil, @"%@ north", @"North latitude format, medium: {latitude}");
            negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_MEDIUM", @"Foundation", nil, @"%@ south", @"South latitude format, medium: {latitude}");
            positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_MEDIUM", @"Foundation", nil, @"%@ east", @"East longitude format, medium: {longitude}");
            negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_MEDIUM", @"Foundation", nil, @"%@ west", @"West longitude format, medium: {longitude}");
            stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_MEDIUM", @"Foundation", nil, @"%@, %@", @"Coordinate pair format, medium: {latitude}, {longitude}");
            break;
            
        case NSFormattingUnitStyleLong:
            positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_LONG", @"Foundation", nil, @"%@ north", @"North latitude format, long: {latitude}");
            negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_LONG", @"Foundation", nil, @"%@ south", @"South latitude format, long: {latitude}");
            positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_LONG", @"Foundation", nil, @"%@ east", @"East longitude format, long: {longitude}");
            negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_LONG", @"Foundation", nil, @"%@ west", @"West longitude format, long: {longitude}");
            stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_LONG", @"Foundation", nil, @"%@ by %@", @"Coordinate pair format, long: {latitude}, {longitude}");
            break;
    }
    NSString *latitudeString = [self stringFromLocationDegrees:coordinate.latitude
                                                positiveFormat:positiveLatitudeFormat
                                                negativeFormat:negativeLatitudeFormat];
    NSString *longitudeString = [self stringFromLocationDegrees:coordinate.longitude
                                                 positiveFormat:positiveLongitudeFormat
                                                 negativeFormat:negativeLongitudeFormat];
    return [NSString stringWithFormat:stringFormat, latitudeString, longitudeString];
}

- (NSString *)stringFromLocationDegrees:(CLLocationDegrees)degrees positiveFormat:(NSString *)positiveFormat negativeFormat:(NSString *)negativeFormat {
    CLLocationDegrees minutes = (fabs(degrees) - floor(fabs(degrees))) * 60;
    CLLocationDegrees seconds = (minutes - floor(minutes)) * 60;
    
    NSString *degreesFormat;
    NSString *minutesFormat;
    NSString *secondsFormat;
    NSString *degreesMinutesFormat;
    NSString *degreesMinutesSecondsFormat;
    switch (self.unitStyle) {
        case NSFormattingUnitStyleShort:
            degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_SHORT", @"Foundation", nil, @"%d°", @"Degrees format, short: {degrees}");
            minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_SHORT", @"Foundation", nil, @"%d′", @"Minutes format, short: {minutes}");
            secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_SHORT", @"Foundation", nil, @"%d″", @"Seconds format, short: {seconds}");
            degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_SHORT", @"Foundation", nil, @"%@%@", @"Coordinate format, short: {degrees}{minutes}");
            degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_SHORT", @"Foundation", nil, @"%@%@%@", @"Coordinate format, short: {degrees}{minutes}{seconds}");
            break;
            
        case NSFormattingUnitStyleMedium:
            degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_MEDIUM", @"Foundation", nil, @"%d°", @"Degrees format, medium: {degrees}");
            minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_MEDIUM", @"Foundation", nil, @"%d′", @"Minutes format, medium: {minutes}");
            secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_MEDIUM", @"Foundation", nil, @"%d″", @"Seconds format, medium: {seconds}");
            degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_MEDIUM", @"Foundation", nil, @"%@%@", @"Coordinate format, medium: {degrees}{minutes}");
            degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_MEDIUM", @"Foundation", nil, @"%@%@%@", @"Coordinate format, medium: {degrees}{minutes}{seconds}");
            break;
            
        case NSFormattingUnitStyleLong:
            degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_LONG", @"Foundation", nil, @"%d degree(s)", @"Degrees format, long");
            minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_LONG", @"Foundation", nil, @"%d minute(s)", @"Minutes format, long");
            secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_LONG", @"Foundation", nil, @"%d second(s)", @"Seconds format, long");
            degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_LONG", @"Foundation", nil, @"%@ and %@", @"Coordinate format, long: {degrees}{minutes}");
            degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_LONG", @"Foundation", nil, @"%@, %@, and %@", @"Coordinate format, long: {degrees}{minutes}{seconds}");
            break;
    }
    
    NSString *degreesString = [NSString stringWithFormat:degreesFormat, (int)floor(fabs(degrees))];
    
    NSString *string;
    if (trunc(seconds) > 0 && self.allowsSeconds) {
        NSString *minutesString = [NSString stringWithFormat:minutesFormat, (int)floor(minutes)];
        NSString *secondsString = [NSString stringWithFormat:secondsFormat, (int)round(seconds)];
        string = [NSString stringWithFormat:degreesMinutesSecondsFormat,
                  degreesString, minutesString, secondsString];
    } else if (trunc(minutes) > 0 && self.allowsMinutes) {
        NSString *minutesString = [NSString stringWithFormat:minutesFormat, (int)round(minutes)];
        string = [NSString stringWithFormat:degreesMinutesFormat,
                  degreesString, minutesString];
    } else {
        string = [NSString stringWithFormat:degreesFormat, (int)round(fabs(degrees))];
    }
    
    if (degrees == 0) {
        return string;
    }
    return [NSString stringWithFormat:degrees > 0 ? positiveFormat : negativeFormat, string];
}

- (nullable NSString *)stringForObjectValue:(id)obj {
    if (![obj isKindOfClass:[NSValue class]]) {
        return nil;
    }
    return [self stringFromCoordinate:[obj MGLCoordinateValue]];
}

- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error {
    NSAssert(NO, @"-getObjectValue:forString:errorDescription: has not been implemented");
    return NO;
}

@end
