//
//  YSRACPassthroughSubscriber.m
//  ReactiveObjC
//
//  Created by Justin Spahr-Summers on 2013-06-13.
//  Copyright (c) 2013 GitHub, Inc. All rights reserved.
//

#import "YSRACPassthroughSubscriber.h"
#import "YSRACCompoundDisposable.h"
#import "YSRACSignal.h"
#import "YSRACSignalProvider.h"

#if !defined(DTYSRACE_PROBES_DISABLED) || !DTYSRACE_PROBES_DISABLED

static const char *cleanedDTraceString(NSString *original) {
	return [original stringByReplacingOccurrencesOfString:@"\\s+" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, original.length)].UTF8String;
}

static const char *cleanedSignalDescription(YSRACSignal *signal) {
	NSString *desc = signal.description;

	NSRange range = [desc rangeOfString:@" name:"];
	if (range.location != NSNotFound) {
		desc = [desc stringByReplacingCharactersInRange:range withString:@""];
	}

	return cleanedDTraceString(desc);
}

#endif

@interface YSRACPassthroughSubscriber ()

// The subscriber to which events should be forwarded.
@property (nonatomic, strong, readonly) id<YSRACSubscriber> innerSubscriber;

// The signal sending events to this subscriber.
//
// This property isn't `weak` because it's only used for DTrace probes, so
// a zeroing weak reference would incur an unnecessary performance penalty in
// normal usage.
@property (nonatomic, unsafe_unretained, readonly) YSRACSignal *signal;

// A disposable representing the subscription. When disposed, no further events
// should be sent to the `innerSubscriber`.
@property (nonatomic, strong, readonly) YSRACCompoundDisposable *disposable;

@end

@implementation YSRACPassthroughSubscriber

#pragma mark Lifecycle

- (instancetype)initWithSubscriber:(id<YSRACSubscriber>)subscriber signal:(YSRACSignal *)signal disposable:(YSRACCompoundDisposable *)disposable {
	NSCParameterAssert(subscriber != nil);

	self = [super init];

	_innerSubscriber = subscriber;
	_signal = signal;
	_disposable = disposable;

	[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
	return self;
}

#pragma mark YSRACSubscriber

- (void)sendNext:(id)value {
	if (self.disposable.disposed) return;

	if (YSRACSIGNAL_NEXT_ENABLED()) {
		YSRACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
	}

	[self.innerSubscriber sendNext:value];
}

- (void)sendError:(NSError *)error {
	if (self.disposable.disposed) return;

	if (YSRACSIGNAL_ERROR_ENABLED()) {
		YSRACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description));
	}

	[self.innerSubscriber sendError:error];
}

- (void)sendCompleted {
	if (self.disposable.disposed) return;

	if (YSRACSIGNAL_COMPLETED_ENABLED()) {
		YSRACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description));
	}

	[self.innerSubscriber sendCompleted];
}

- (void)didSubscribeWithDisposable:(YSRACCompoundDisposable *)disposable {
	if (disposable != self.disposable) {
		[self.disposable addDisposable:disposable];
	}
}

@end
