//
//  YSRACEvent.m
//  ReactiveObjC
//
//  Created by Justin Spahr-Summers on 2013-01-07.
//  Copyright (c) 2013 GitHub, Inc. All rights reserved.
//

#import "YSRACEvent.h"

@interface YSRACEvent ()

// An object associated with this event. This will be used for the error and
// value properties.
@property (nonatomic, strong, readonly) id object;

// Initializes the receiver with the given type and object.
- (instancetype)initWithEventType:(YSRACEventType)type object:(id)object;

@end

@implementation YSRACEvent

#pragma mark Properties

- (BOOL)isFinished {
	return self.eventType == YSRACEventTypeCompleted || self.eventType == YSRACEventTypeError;
}

- (NSError *)error {
	return (self.eventType == YSRACEventTypeError ? self.object : nil);
}

- (id)value {
	return (self.eventType == YSRACEventTypeNext ? self.object : nil);
}

#pragma mark Lifecycle

+ (instancetype)completedEvent {
	static dispatch_once_t pred;
	static id singleton;

	dispatch_once(&pred, ^{
		singleton = [[self alloc] initWithEventType:YSRACEventTypeCompleted object:nil];
	});

	return singleton;
}

+ (instancetype)eventWithError:(NSError *)error {
	return [[self alloc] initWithEventType:YSRACEventTypeError object:error];
}

+ (instancetype)eventWithValue:(id)value {
	return [[self alloc] initWithEventType:YSRACEventTypeNext object:value];
}

- (instancetype)initWithEventType:(YSRACEventType)type object:(id)object {
	self = [super init];

	_eventType = type;
	_object = object;

	return self;
}

#pragma mark NSCopying

- (id)copyWithZone:(NSZone *)zone {
	return self;
}

#pragma mark NSObject

- (NSString *)description {
	NSString *eventDescription = nil;

	switch (self.eventType) {
		case YSRACEventTypeCompleted:
			eventDescription = @"completed";
			break;

		case YSRACEventTypeError:
			eventDescription = [NSString stringWithFormat:@"error = %@", self.object];
			break;

		case YSRACEventTypeNext:
			eventDescription = [NSString stringWithFormat:@"next = %@", self.object];
			break;

		default:
			NSCAssert(NO, @"Unrecognized event type: %i", (int)self.eventType);
	}

	return [NSString stringWithFormat:@"<%@: %p>{ %@ }", self.class, self, eventDescription];
}

- (NSUInteger)hash {
	return self.eventType ^ [self.object hash];
}

- (BOOL)isEqual:(id)event {
	if (event == self) return YES;
	if (![event isKindOfClass:YSRACEvent.class]) return NO;
	if (self.eventType != [event eventType]) return NO;

	// Catches the nil case too.
	return self.object == [event object] || [self.object isEqual:[event object]];
}

@end
