//
//  main.m
//  kvohow
//
#import <Foundation/Foundation.h>


/*	Data class to hold the mutated data	*/
@interface DataClass : NSObject
{
@private
	NSString* value;
	NSMutableSet* _setData;
}
@property (readonly,nonatomic,retain) NSString* value;

-(id)init;
+(id)DataClassFromValue:(NSString*)value;
@end

@implementation DataClass
+(id)DataClassFromValue:(NSString*)value
{
	DataClass* dc = [[DataClass alloc]init];
	[dc setValue:value];
	return [dc autorelease];
}
-(id)init
{
	if( ! (self=[super init]) )
		return self;
	
	_setData = [NSMutableSet setWithObjects:@"object one", nil];
	return self;
}

/*	override to declare manual notifications for the 'data' keypath		*/
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
{
	if( [theKey isEqualToString:@"data"])
		return NO;
	
	return [super automaticallyNotifiesObserversForKey:theKey];
}

/*	a property with the keypath name is still required	*/
-(NSSet*)data
{
	NSLog( @">>DataClass 'data' property has been queried.");
	return _setData;
}

-(id)addToSet:(id)object
{
	NSString* key = @"data";
	NSSet* objects = [NSSet setWithObjects:object, nil];
	
	NSLog( @"** addToSet - willChangeValueForKey" );
	[self willChangeValueForKey:key
				withSetMutation:NSKeyValueUnionSetMutation /*Indicates that objects in the specified set are being added to the receiver*/
				   usingObjects:objects];
	
		[_setData addObject:object];
	
	NSLog( @"** addToSet - didChangeValueForKey" );
	[self didChangeValueForKey:key
			   withSetMutation:NSKeyValueUnionSetMutation
				  usingObjects:objects];
	
	NSLog( @"** addToSet COMPLETE ****************" );
	return self;
}
-(BOOL)removeFromSet:(id)object
{
	if( NO==[_setData containsObject:object] )
		return NO;
	
	NSString* key = @"data";
	NSSet* objects = [NSSet setWithObjects:object, nil];
	
	NSLog( @"** removeFromSet - willChangeValueForKey" );
	[self willChangeValueForKey:key
				withSetMutation:NSKeyValueMinusSetMutation /*Indicates that the objects in the specified set are being removed from the receiver.*/
				   usingObjects:objects];
	
		[_setData removeObject:object];
	
	NSLog( @"** removeFromSet - didChangeValueForKey" );
	[self didChangeValueForKey:key
			   withSetMutation:NSKeyValueMinusSetMutation
				  usingObjects:objects];
	
	NSLog( @"** removeFromSet COMPLETE ****************" );
	return YES;
}
-(BOOL)replaceInSet:(id)object with:(id)other
{
	if( NO==[_setData containsObject:object] )
		return NO;
	if( YES==[_setData containsObject:other] )
		return NO;
	
	NSString* key = @"data";
	NSSet* objects = [NSSet setWithObjects:object, nil];
	
	NSLog( @"** replaceInSet - willChangeValueForKey" );
	[self willChangeValueForKey:key
				withSetMutation:NSKeyValueMinusSetMutation /*Indicates that the objects in the specified set are being removed from the receiver.*/
				   usingObjects:objects];
	
		[_setData removeObject:object];
		[_setData addObject:other];

	NSLog( @"** replaceInSet - didChangeValueForKey" );
	[self didChangeValueForKey:key
			   withSetMutation:NSKeyValueMinusSetMutation
				  usingObjects:objects];
	
	NSLog( @"** replaceInSet COMPLETE ****************" );
	return YES;
}

-(NSString*)value
{
	NSLog( @">>DataClass 'value' property has been queried.");
	return value;
}
-(void)setValue:(NSString*)newValue
{
	NSLog( @">>DataClass setValue '%@' now renamed to '%@'", value,newValue );
	if(value)
		[value release];
	
	value = [newValue retain];
}
@end

/*	Observer class to watch changes	*/
@interface Observer : NSObject
{
}
@end
@implementation Observer
-(void)observeValueForKeyPath:(NSString*)keyPath
					 ofObject:(id)object
					   change:(NSDictionary*)change
					  context:(void*)context
{
	NSLog( @"Observer: keyPath '%@' change '%@'", keyPath,change  );
}
@end

int main(int argc, const char * argv[])
{
	@autoreleasepool {
		Observer* observer = [[Observer alloc]init];
		DataClass* someData = [DataClass DataClassFromValue:@"initial value"];
		
		someData.value = @"changed value";
		
	    [someData addObserver:observer
			   forKeyPath:@"value"
				  options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionPrior
				  context:NULL ];
		[someData addObserver:observer
				   forKeyPath:@"data"
					  options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionPrior
					  context:NULL ];
		
		/* Automatic kvo notifications				*/
	    someData.value = @"observed value";
		
		/* Manual kvo notifications by DataClass	*/
		NSLog( @"  -- Manual Notifications --  " );
		NSString* stringValue = @"A New Object";
		NSString* stringOtherValue = @"Something else";
		[someData addToSet:stringValue ];		/*	Add */
		[someData replaceInSet:stringValue with:stringOtherValue ];
		[someData removeFromSet:stringOtherValue ];  /*	Delete */
		
		
		[someData removeObserver:observer forKeyPath:@"value" ];
		[someData removeObserver:observer forKeyPath:@"data" ];
	}
    return 0;
}

