//===--- FoundationBridge.m -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#import "FoundationBridge.h"
#import <objc/runtime.h>

@implementation ObjectBehaviorVerifier {
    NSMutableArray *_actions;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _actions = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)dealloc {
    [_actions release];
    [super dealloc];
}

- (void)appendAction:(ObjectBehaviorAction)action {
    [_actions addObject:@(action)];
    switch (action) {
        case ObjectBehaviorActionRetain:
            _wasRetained = YES;
            break;
        case ObjectBehaviorActionMutableCopy:
            _wasMutableCopied = YES;
            // fall through
        case ObjectBehaviorActionCopy:
            _wasCopied = YES;
            break;
    }
}

- (void)enumerate:(void (^)(ObjectBehaviorAction))block {
    for (NSNumber *action in _actions) {
        block((ObjectBehaviorAction)action.integerValue);
    }
}

- (void)reset {
    [_actions removeAllObjects];
    _wasRetained = NO;
    _wasMutableCopied = NO;
    _wasCopied = NO;
}

- (void)dump {
    [self enumerate:^(ObjectBehaviorAction action) {
        switch (action) {
            case ObjectBehaviorActionRetain:
                printf("retain\n");
                break;
            case ObjectBehaviorActionMutableCopy:
                printf("mutableCopy\n");
                break;
            case ObjectBehaviorActionCopy:
                printf("copy\n");
                break;
        }
    }];
}

@end


@implementation ImmutableDataVerifier

- (instancetype)init {
    self = [super init];
    if (self) {
        _verifier = [[ObjectBehaviorVerifier alloc] init];
        char *bytes = "hello world";
        _data = [[NSData alloc] initWithBytes:bytes length:strlen(bytes)];
    }
    return self;
}

- (instancetype)initWithData:(NSData *)data verifier:(ObjectBehaviorVerifier *)verifier {
    self = [super init];
    if (self) {
        _verifier = [verifier retain];
        _data = [data mutableCopyWithZone:nil];
    }
    return self;
}

- (void)dealloc {
    [_verifier release];
    [_data release];
    [super dealloc];
}

- (id)retain {
    [_verifier appendAction:ObjectBehaviorActionRetain];
    return [super retain];
}

- (id)copyWithZone:(NSZone *)zone {
    [_verifier appendAction:ObjectBehaviorActionCopy];
    return [super retain];
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    [_verifier appendAction:ObjectBehaviorActionMutableCopy];
    return [[MutableDataVerifier alloc] initWithData:_data verifier:_verifier];
}

- (NSUInteger)length {
    return _data.length;
}

- (const void *)bytes {
    return _data.bytes;
}

- (NSData *)subdataWithRange:(NSRange)range {
    return [_data subdataWithRange:range];
}

@end

@implementation MutableDataVerifier

- (instancetype)init {
    self = [super init];
    if (self) {
        _verifier = [[ObjectBehaviorVerifier alloc] init];
        char *bytes = "hello world";
        _data = [[NSMutableData alloc] initWithBytes:bytes length:strlen(bytes)];
    }
    return self;
}

- (instancetype)initWithData:(NSData *)data verifier:(ObjectBehaviorVerifier *)verifier {
    self = [super init];
    if (self) {
        _verifier = [verifier retain];
        _data = [data mutableCopyWithZone:nil];
    }
    return self;
}

- (void)dealloc {
    [_verifier release];
    [_data release];
    [super dealloc];
}

- (id)retain {
    [_verifier appendAction:ObjectBehaviorActionRetain];
    return [super retain];
}

- (id)copyWithZone:(NSZone *)zone {
    [_verifier appendAction:ObjectBehaviorActionCopy];
    return [[ImmutableDataVerifier alloc] initWithData:_data verifier:_verifier];
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    [_verifier appendAction:ObjectBehaviorActionMutableCopy];
    return [[MutableDataVerifier alloc] initWithData:_data verifier:_verifier];
}

- (NSUInteger)length {
    return _data.length;
}

- (void)setLength:(NSUInteger)length {
    _data.length = length;
}

- (const void *)bytes {
    return _data.bytes;
}

- (void *)mutableBytes {
    return _data.mutableBytes;
}

- (void)appendBytes:(const void *)bytes length:(NSUInteger)length {
    [_data appendBytes:bytes length:length];
}

- (NSData *)subdataWithRange:(NSRange)range {
    return [_data subdataWithRange:range];
}

@end

void takesData(NSData *object) {
    // do NOTHING here...
}

NSData *returnsData() {
    static dispatch_once_t once = 0L;
    static NSData *data = nil;
    dispatch_once(&once, ^{
        char *bytes = "hello world";
        data = [[NSData alloc] initWithBytes:bytes length:strlen(bytes)];
    });
    return data;
}

BOOL identityOfData(NSData *data) {
    return data == returnsData();
}


@implementation CalendarBridgingTester

- (NSCalendar *)autoupdatingCurrentCalendar {
    return [NSCalendar autoupdatingCurrentCalendar];
}

- (BOOL)verifyAutoupdatingCalendar:(NSCalendar *)calendar {
    Class autoCalendarClass = (Class)objc_lookUpClass("_NSAutoCalendar");
    if (autoCalendarClass && [calendar isKindOfClass:autoCalendarClass]) {
        return YES;
    } else {
        autoCalendarClass = (Class)objc_lookUpClass("NSAutoCalendar");
        return [calendar isKindOfClass:autoCalendarClass];
    }
}

@end

@implementation TimeZoneBridgingTester

- (NSTimeZone *)autoupdatingCurrentTimeZone {
    return [NSTimeZone localTimeZone];
}

- (BOOL)verifyAutoupdatingTimeZone:(NSTimeZone *)tz {
    Class autoTimeZoneClass = (Class)objc_lookUpClass("__NSLocalTimeZone");
    return [tz isKindOfClass:autoTimeZoneClass];

}
@end

@implementation LocaleBridgingTester

- (NSLocale *)autoupdatingCurrentLocale {
    return [NSLocale autoupdatingCurrentLocale];
}

- (BOOL)verifyAutoupdatingLocale:(NSLocale *)locale {
    Class autoLocaleClass = (Class)objc_lookUpClass("NSAutoLocale");
    return [locale isKindOfClass:autoLocaleClass];
}

@end

@implementation NumberBridgingTester

- (BOOL)verifyKeysInRange:(NSRange)range existInDictionary:(NSDictionary *)dictionary {
    for (NSUInteger i = 0; i < range.length; i += 1) {
        if (!dictionary[@(range.location + i)]) {
            return NO;
        }
    }

    return YES;
}

@end
