/*
 Copyright (c) 2009-2017, Haystack Software LLC https://www.arqbackup.com
 
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 
 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 
 * Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions and the following disclaimer in the
 documentation and/or other materials provided with the distribution.
 
 * Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
 their contributors may be used to endorse or promote products derived from
 this software without specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */



#import "AWSRegion.h"
#import "RegexKitLite.h"



@implementation AWSRegion
+ (NSArray *)allRegions {
    return [NSArray arrayWithObjects:
            [AWSRegion usEast1],
            [AWSRegion usEast2],
            [AWSRegion usWest1],
            [AWSRegion usWest2],
            [AWSRegion euWest1],
            [AWSRegion euCentral1],
            [AWSRegion apSoutheast1],
            [AWSRegion apSoutheast2],
            [AWSRegion apNortheast1],
            [AWSRegion apNortheast2],
            [AWSRegion apSouth1],
            [AWSRegion saEast1],
            nil];
}
+ (NSArray *)s3Regions {
    return [AWSRegion allRegions];
}
+ (NSArray *)glacierRegions {
    NSMutableArray *ret = [NSMutableArray array];
    for (AWSRegion *region in [AWSRegion allRegions]) {
        if ([region supportsGlacier]) {
            [ret addObject:region];
        }
    }
    return ret;
}
+ (AWSRegion *)regionWithName:(NSString *)theRegionName {
    for (AWSRegion *region in [AWSRegion allRegions]) {
        if ([[region regionName] isEqualToString:theRegionName]) {
            return region;
        }
    }
    return nil;
}
+ (AWSRegion *)regionWithLocation:(NSString *)theLocation {
    if ([theLocation length] == 0) {
        return [AWSRegion usEast1];
    }
    
    for (AWSRegion *region in [AWSRegion allRegions]) {
        for (NSString *constraint in [region s3LocationConstraints]) {
            if ([constraint caseInsensitiveCompare:theLocation] == NSOrderedSame) {
                return region;
            }
        }
    }
    HSLogDebug(@"no AWS region found for location %@", theLocation);
    return nil;
}
+ (AWSRegion *)regionWithS3Endpoint:(NSURL *)theEndpoint {
    if (![[theEndpoint scheme] isEqualToString:@"https"] && ![[theEndpoint scheme] isEqualToString:@"http"]) {
//        HSLogDebug(@"unknown AWSRegion endpoint scheme: %@", theEndpoint);
        return nil;
    }
    
    for (AWSRegion *region in [AWSRegion allRegions]) {
        NSURL *endpoint = [region s3EndpointWithSSL:YES];
        if ([[theEndpoint host] isEqualToString:[endpoint host]]) {
            return region;
        }
    }
//    HSLogDebug(@"AWSRegion not found for S3 endpoint %@", theEndpoint);
    return nil;
}
+ (AWSRegion *)usEast1 {
    return [[[AWSRegion alloc] initWithRegionName:@"us-east-1"
                            s3LocationConstraints:nil
                                       s3Hostname:@"s3.amazonaws.com"
                                      displayName:@"US East (Northern Virginia)"
                                 shortDisplayName:@"Virginia"
               s3StorageDollarsPerGBMonthStandard:.023
                    s3StorageDollarsPerGBMonthRRS:.0125
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.004
                        glacierUploadDollarsPerGB:.05
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)usEast2 {
    return [[[AWSRegion alloc] initWithRegionName:@"us-east-2"
                            s3LocationConstraints:[NSArray arrayWithObject:@"us-east-2"]
                                       s3Hostname:@"s3-us-east-2.amazonaws.com"
                                      displayName:@"US East (Ohio)"
                                 shortDisplayName:@"Ohio"
               s3StorageDollarsPerGBMonthStandard:.023
                    s3StorageDollarsPerGBMonthRRS:.0125
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.004
                        glacierUploadDollarsPerGB:.05
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)usWest1 {
    return [[[AWSRegion alloc] initWithRegionName:@"us-west-1"
                            s3LocationConstraints:[NSArray arrayWithObject:@"us-west-1"]
                                       s3Hostname:@"s3-us-west-1.amazonaws.com"
                                      displayName:@"US West (Northern California)"
                                 shortDisplayName:@"California"
               s3StorageDollarsPerGBMonthStandard:.026
                    s3StorageDollarsPerGBMonthRRS:.019
                             s3UploadDollarsPerGB:.0055
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.005
                        glacierUploadDollarsPerGB:.055
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)usWest2 {
    return [[[AWSRegion alloc] initWithRegionName:@"us-west-2"
                            s3LocationConstraints:[NSArray arrayWithObject:@"us-west-2"]
                                       s3Hostname:@"s3-us-west-2.amazonaws.com"
                                      displayName:@"US West (Oregon)"
                                 shortDisplayName:@"Oregon"
               s3StorageDollarsPerGBMonthStandard:.023
                    s3StorageDollarsPerGBMonthRRS:.0125
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.004
                        glacierUploadDollarsPerGB:.05
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)euWest1 {
    return [[[AWSRegion alloc] initWithRegionName:@"eu-west-1"
                            s3LocationConstraints:[NSArray arrayWithObjects:@"EU", @"eu-west-1", nil]
                                       s3Hostname:@"s3-eu-west-1.amazonaws.com"
                                      displayName:@"EU (Ireland)"
                                 shortDisplayName:@"Ireland"
               s3StorageDollarsPerGBMonthStandard:.023
                    s3StorageDollarsPerGBMonthRRS:.0125
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.004
                        glacierUploadDollarsPerGB:.055
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)euCentral1 {
    return [[[AWSRegion alloc] initWithRegionName:@"eu-central-1"
                            s3LocationConstraints:[NSArray arrayWithObjects:@"eu-central-1", nil]
                                       s3Hostname:@"s3.eu-central-1.amazonaws.com"
                                      displayName:@"EU (Frankfurt)"
                                 shortDisplayName:@"Frankfurt"
               s3StorageDollarsPerGBMonthStandard:.0245
                    s3StorageDollarsPerGBMonthRRS:.0135
                             s3UploadDollarsPerGB:.0054
                    s3DataTransferOutDollarsPerGB:.09
                  glacierStorageDollarsPerGBMonth:.0045
                        glacierUploadDollarsPerGB:.06
               glacierDataTransferOutDollarsPerGB:.09
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)apSoutheast1 {
    return [[[AWSRegion alloc] initWithRegionName:@"ap-southeast-1"
                            s3LocationConstraints:[NSArray arrayWithObject:@"ap-southeast-1"]
                                       s3Hostname:@"s3-ap-southeast-1.amazonaws.com"
                                      displayName:@"Asia Pacific (Singapore)"
                                 shortDisplayName:@"Singapore"
               s3StorageDollarsPerGBMonthStandard:.025
                    s3StorageDollarsPerGBMonthRRS:.02
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.12
                  glacierStorageDollarsPerGBMonth:0
                        glacierUploadDollarsPerGB:0
               glacierDataTransferOutDollarsPerGB:0
                                  supportsGlacier:NO] autorelease];
}
+ (AWSRegion *)apSoutheast2 {
    return [[[AWSRegion alloc] initWithRegionName:@"ap-southeast-2"
                            s3LocationConstraints:[NSArray arrayWithObject:@"ap-southeast-2"]
                                       s3Hostname:@"s3-ap-southeast-2.amazonaws.com"
                                      displayName:@"Asia Pacific (Sydney)"
                                 shortDisplayName:@"Sydney"
               s3StorageDollarsPerGBMonthStandard:.025
                    s3StorageDollarsPerGBMonthRRS:.019
                             s3UploadDollarsPerGB:.0055
                    s3DataTransferOutDollarsPerGB:.14
                  glacierStorageDollarsPerGBMonth:.005
                        glacierUploadDollarsPerGB:.06
               glacierDataTransferOutDollarsPerGB:.14
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)apNortheast1 {
    return [[[AWSRegion alloc] initWithRegionName:@"ap-northeast-1"
                            s3LocationConstraints:[NSArray arrayWithObject:@"ap-northeast-1"]
                                       s3Hostname:@"s3-ap-northeast-1.amazonaws.com"
                                      displayName:@"Asia Pacific (Tokyo)"
                                 shortDisplayName:@"Tokyo"
               s3StorageDollarsPerGBMonthStandard:.025
                    s3StorageDollarsPerGBMonthRRS:.019
                             s3UploadDollarsPerGB:.005
                    s3DataTransferOutDollarsPerGB:.14
                  glacierStorageDollarsPerGBMonth:.005
                        glacierUploadDollarsPerGB:.06
               glacierDataTransferOutDollarsPerGB:.14
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)apNortheast2 {
    return [[[AWSRegion alloc] initWithRegionName:@"ap-northeast-2"
                            s3LocationConstraints:[NSArray arrayWithObject:@"ap-northeast-2"]
                                       s3Hostname:@"s3-ap-northeast-2.amazonaws.com"
                                      displayName:@"Asia Pacific (Seoul)"
                                 shortDisplayName:@"Seoul"
               s3StorageDollarsPerGBMonthStandard:.025
                    s3StorageDollarsPerGBMonthRRS:.018
                             s3UploadDollarsPerGB:.0045
                    s3DataTransferOutDollarsPerGB:.126
                  glacierStorageDollarsPerGBMonth:.005
                        glacierUploadDollarsPerGB:.06
               glacierDataTransferOutDollarsPerGB:.126
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)apSouth1 {
    return [[[AWSRegion alloc] initWithRegionName:@"ap-south-1"
                            s3LocationConstraints:[NSArray arrayWithObject:@"ap-south-1"]
                                       s3Hostname:@"s3-ap-south-1.amazonaws.com"
                                      displayName:@"Asia Pacific (Mumbai)"
                                 shortDisplayName:@"Mumbai"
               s3StorageDollarsPerGBMonthStandard:.025
                    s3StorageDollarsPerGBMonthRRS:.019
                             s3UploadDollarsPerGB:.0045
                    s3DataTransferOutDollarsPerGB:.126
                  glacierStorageDollarsPerGBMonth:.005
                        glacierUploadDollarsPerGB:.06
               glacierDataTransferOutDollarsPerGB:.126
                                  supportsGlacier:YES] autorelease];
}
+ (AWSRegion *)saEast1 {
    return [[[AWSRegion alloc] initWithRegionName:@"sa-east-1"
                            s3LocationConstraints:[NSArray arrayWithObject:@"sa-east-1"]
                                       s3Hostname:@"s3-sa-east-1.amazonaws.com"
                                      displayName:@"South America (Sao Paulo)"
                                 shortDisplayName:@"Sao Paulo"
               s3StorageDollarsPerGBMonthStandard:.0405
                    s3StorageDollarsPerGBMonthRRS:.026
                             s3UploadDollarsPerGB:.007
                    s3DataTransferOutDollarsPerGB:.25
                  glacierStorageDollarsPerGBMonth:0
                        glacierUploadDollarsPerGB:0
               glacierDataTransferOutDollarsPerGB:0
                                  supportsGlacier:NO] autorelease];
}


- (void)dealloc {
    [regionName release];
    [s3LocationConstraints release];
    [s3Hostname release];
    [displayName release];
    [shortDisplayName release];
    [super dealloc];
}

- (NSString *)regionName {
    return regionName;
}
- (NSString *)displayName {
    return displayName;
}
- (NSString *)shortDisplayName {
    return shortDisplayName;
}
- (NSString *)defaultS3LocationConstraint {
    return [s3LocationConstraints lastObject];
}
- (NSArray *)s3LocationConstraints {
    return s3LocationConstraints;
}
- (double)s3StorageDollarsPerGBMonthStandard {
    return s3StorageDollarsPerGBMonthStandard;
}
- (double)s3StorageDollarsPerGBMonthRRS {
    return s3StorageDollarsPerGBMonthRRS;
}
- (double)s3UploadDollarsPerGB {
    return s3UploadDollarsPerGB;
}
- (double)s3DataTransferOutDollarsPerGB {
    return s3DataTransferOutDollarsPerGB;
}
- (double)glacierStorageDollarsPerGBMonth {
    return glacierStorageDollarsPerGBMonth;
}
- (double)glacierUploadDollarsPerGB {
    return glacierUploadDollarsPerGB;
}
- (double)glacierDataTransferOutDollarsPerGB {
    return glacierDataTransferOutDollarsPerGB;
}
- (NSURL *)s3EndpointWithSSL:(BOOL)useSSL {
    return [NSURL URLWithString:[NSString stringWithFormat:@"http%@://%@", (useSSL ? @"s" : @""), s3Hostname]];
}
- (BOOL)supportsGlacier {
    return supportsGlacier;
}
- (NSString *)glacierEndpointWithSSL:(BOOL)useSSL {
    if (!supportsGlacier) {
        return nil;
    }
    if (getenv("AWS_HOST")) {
        return [NSString stringWithFormat:@"%s/aws/glacier", getenv("AWS_HOST")];
    }
    return [self endpointWithService:@"glacier" useSSL:useSSL];
}
- (NSString *)snsEndpointWithSSL:(BOOL)useSSL {
    if (getenv("AWS_HOST")) {
        return [NSString stringWithFormat:@"%s/aws/sns", getenv("AWS_HOST")];
    }
    return [self endpointWithService:@"sns" useSSL:useSSL];
}
- (NSString *)sqsEndpointWithSSL:(BOOL)useSSL {
    if (getenv("AWS_HOST")) {
        return [NSString stringWithFormat:@"%s/aws/sqs", getenv("AWS_HOST")];
    }
    return [self endpointWithService:@"sqs" useSSL:useSSL];
}


#pragma mark NSObject
- (NSString *)description {
    return [NSString stringWithFormat:@"<AWSRegion %@>", regionName];
}
- (BOOL)isEqual:(id)other {
    if (other == self) {
        return YES;
    }
    if (other == nil || ![other isKindOfClass:[self class]]) {
        return NO;
    }
    return [regionName isEqualToString:[(AWSRegion *)other regionName]];
}
- (NSUInteger)hash {
    return [regionName hash];
}

#pragma mark NSCopying
- (id)copyWithZone:(NSZone *)zone {
    return [[AWSRegion alloc] initWithRegionName:regionName
                           s3LocationConstraints:s3LocationConstraints
                                      s3Hostname:s3Hostname
                                     displayName:displayName
                                shortDisplayName:shortDisplayName
              s3StorageDollarsPerGBMonthStandard:s3StorageDollarsPerGBMonthStandard
                   s3StorageDollarsPerGBMonthRRS:s3StorageDollarsPerGBMonthRRS
                            s3UploadDollarsPerGB:s3UploadDollarsPerGB
                   s3DataTransferOutDollarsPerGB:s3DataTransferOutDollarsPerGB
                 glacierStorageDollarsPerGBMonth:glacierStorageDollarsPerGBMonth
                       glacierUploadDollarsPerGB:glacierUploadDollarsPerGB
              glacierDataTransferOutDollarsPerGB:glacierDataTransferOutDollarsPerGB
                                 supportsGlacier:supportsGlacier];
}


#pragma mark internal
- (id)initWithRegionName:(NSString *)theRegionName
   s3LocationConstraints:(NSArray *)theS3LocationConstraints
              s3Hostname:(NSString *)theS3Hostname
             displayName:(NSString *)theDisplayName
        shortDisplayName:(NSString *)theShortDisplayName
s3StorageDollarsPerGBMonthStandard:(double)theS3StorageDollarsPerGBMonthStandard
  s3StorageDollarsPerGBMonthRRS:(double)theS3StorageDollarsPerGBMonthRRS
    s3UploadDollarsPerGB:(double)theS3UploadDollarsPerGB
s3DataTransferOutDollarsPerGB:(double)theS3DataTransferOutDollarsPerGB
glacierStorageDollarsPerGBMonth:(double)theGlacierStorageDollarsPerGBMonth
glacierUploadDollarsPerGB:(double)theGlacierUploadDollarsPerGB
glacierDataTransferOutDollarsPerGB:(double)theGlacierDataTransferOutDollarsPerGB
         supportsGlacier:(BOOL)theSupportsGlacier {
    if (self = [super init]) {
        regionName = [theRegionName retain];
        s3LocationConstraints = [theS3LocationConstraints retain];
        s3Hostname = [theS3Hostname retain];
        displayName = [theDisplayName retain];
        shortDisplayName = [theShortDisplayName retain];
        s3StorageDollarsPerGBMonthStandard = theS3StorageDollarsPerGBMonthStandard;
        s3StorageDollarsPerGBMonthRRS = theS3StorageDollarsPerGBMonthRRS;
        s3UploadDollarsPerGB = theS3UploadDollarsPerGB;
        s3DataTransferOutDollarsPerGB = theS3DataTransferOutDollarsPerGB;
        glacierStorageDollarsPerGBMonth = theGlacierStorageDollarsPerGBMonth;
        glacierUploadDollarsPerGB = theGlacierUploadDollarsPerGB;
        glacierDataTransferOutDollarsPerGB = theGlacierDataTransferOutDollarsPerGB;
        supportsGlacier = theSupportsGlacier;
    }
    return self;
}

- (NSString *)endpointWithService:(NSString *)theServiceName useSSL:(BOOL)useSSL {
    return [NSString stringWithFormat:@"http%@://%@.%@.amazonaws.com", (useSSL ? @"s" : @""), theServiceName, regionName];
}
@end
