//
//  NSObject+YSRACLifting.m
//  ReactiveObjC
//
//  Created by Josh Abernathy on 10/13/12.
//  Copyright (c) 2012 GitHub, Inc. All rights reserved.
//

#import "NSObject+YSRACLifting.h"
#import "YSRACEXTScope.h"
#import "NSInvocation+YSRACTypeParsing.h"
#import "NSObject+YSRACDeallocating.h"
#import "NSObject+YSRACDescription.h"
#import "YSRACSignal+Operations.h"
#import "YSRACTuple.h"

@implementation NSObject (YSRACLifting)

- (YSRACSignal *)ysrac_liftSelector:(SEL)selector withSignalOfArguments:(YSRACSignal *)arguments {
	NSCParameterAssert(selector != NULL);
	NSCParameterAssert(arguments != nil);
	
	@unsafeify(self);
	
	NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
	NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector));
	
	return [[[[arguments
		takeUntil:self.ysrac_willDeallocSignal]
		map:^(YSRACTuple *arguments) {
			@strongify(self);
			
			NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
			invocation.selector = selector;
			invocation.ysrac_argumentsTuple = arguments;
			[invocation invokeWithTarget:self];
			
			return invocation.ysrac_returnValue;
		}]
		replayLast]
		setNameWithFormat:@"%@ -ysrac_liftSelector: %s withSignalsOfArguments: %@", YSRACDescription(self), sel_getName(selector), arguments];
}

- (YSRACSignal *)ysrac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals {
	NSCParameterAssert(signals != nil);
	NSCParameterAssert(signals.count > 0);

	NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
	NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector));

	NSUInteger numberOfArguments __attribute__((unused)) = methodSignature.numberOfArguments - 2;
	NSCAssert(numberOfArguments == signals.count, @"Wrong number of signals for %@ (expected %lu, got %lu)", NSStringFromSelector(selector), (unsigned long)numberOfArguments, (unsigned long)signals.count);

	return [[self
		ysrac_liftSelector:selector withSignalOfArguments:[YSRACSignal combineLatest:signals]]
		setNameWithFormat:@"%@ -ysrac_liftSelector: %s withSignalsFromArray: %@", YSRACDescription(self), sel_getName(selector), signals];
}

- (YSRACSignal *)ysrac_liftSelector:(SEL)selector withSignals:(YSRACSignal *)firstSignal, ... {
	NSCParameterAssert(firstSignal != nil);

	NSMutableArray *signals = [NSMutableArray array];

	va_list args;
	va_start(args, firstSignal);
	for (id currentSignal = firstSignal; currentSignal != nil; currentSignal = va_arg(args, id)) {
		NSCAssert([currentSignal isKindOfClass:YSRACSignal.class], @"Argument %@ is not a YSRACSignal", currentSignal);

		[signals addObject:currentSignal];
	}
	va_end(args);

	return [[self
		ysrac_liftSelector:selector withSignalsFromArray:signals]
		setNameWithFormat:@"%@ -ysrac_liftSelector: %s withSignals: %@", YSRACDescription(self), sel_getName(selector), signals];
}

@end
