//
//  NSUserDefaultsRACSupportSpec.m
//  ReactiveCocoa
//
//  Created by Matt Diephouse on 12/19/13.
//  Copyright (c) 2013 GitHub, Inc. All rights reserved.
//

#import "NSUserDefaults+RACSupport.h"

#import "RACCompoundDisposable.h"
#import "RACDisposable.h"
#import "RACKVOChannel.h"
#import "NSObject+RACDeallocating.h"
#import "RACSignal+Operations.h"

static NSString * const NSUserDefaultsRACSupportSpecStringDefault = @"NSUserDefaultsRACSupportSpecStringDefault";
static NSString * const NSUserDefaultsRACSupportSpecBoolDefault = @"NSUserDefaultsRACSupportSpecBoolDefault";

@interface TestObserver : NSObject

@property (copy, atomic) NSString *string1;
@property (copy, atomic) NSString *string2;

@property (assign, atomic) BOOL bool1;

@end

@implementation TestObserver

@end

SpecBegin(NSUserDefaultsRACSupportSpec)

__block NSUserDefaults *defaults = nil;
__block TestObserver *observer = nil;

beforeEach(^{
	defaults = NSUserDefaults.standardUserDefaults;
	[defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault];
	[defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	observer = [TestObserver new];
});

afterEach(^{
	observer = nil;
});

it(@"should set defaults", ^{
	RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	observer.string1 = @"A string";
	observer.bool1 = YES;
	
	expect([defaults objectForKey:NSUserDefaultsRACSupportSpecStringDefault]).to.equal(@"A string");
	expect([defaults objectForKey:NSUserDefaultsRACSupportSpecBoolDefault]).to.equal(@YES);
});

it(@"should read defaults", ^{
	RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	expect(observer.string1).to.beNil();
	expect(observer.bool1).to.equal(NO);
	
	[defaults setObject:@"Another string" forKey:NSUserDefaultsRACSupportSpecStringDefault];
	[defaults setBool:YES forKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	expect(observer.string1).to.equal(@"Another string");
	expect(observer.bool1).to.equal(YES);
});

it(@"should be okay to create 2 terminals", ^{
	RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	RACChannelTo(observer, string2) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	
	[defaults setObject:@"String 3" forKey:NSUserDefaultsRACSupportSpecStringDefault];
	
	expect(observer.string1).to.equal(@"String 3");
	expect(observer.string2).to.equal(@"String 3");
});

it(@"should handle removed defaults", ^{
	observer.string1 = @"Some string";
	observer.bool1 = YES;
	
	RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	[defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault];
	[defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault];
	
	expect(observer.string1).to.beNil();
	expect(observer.bool1).to.equal(NO);
});

it(@"shouldn't resend values", ^{
	RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	
	RACChannelTo(observer, string1) = terminal;
	
	RACSignal *sentValue = [terminal replayLast];
	observer.string1 = @"Test value";
	id value = [sentValue asynchronousFirstOrDefault:nil success:NULL error:NULL];
	expect(value).to.beNil();
});

it(@"should complete when the NSUserDefaults deallocates", ^{
	__block RACChannelTerminal *terminal;
	__block BOOL deallocated = NO;
	
	@autoreleasepool {
		NSUserDefaults *customDefaults __attribute__((objc_precise_lifetime)) = [NSUserDefaults new];
		[customDefaults.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
			deallocated = YES;
		}]];
		
		terminal = [customDefaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	}
	
	expect(deallocated).to.beTruthy();
	expect([terminal asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
});

it(@"should send an initial value", ^{
	[defaults setObject:@"Initial" forKey:NSUserDefaultsRACSupportSpecStringDefault];
	RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
	expect([terminal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(@"Initial");
});

SpecEnd
