//
//  OxDictionaryController.m
//  AdventureEngine
//
//  Created by Nicholas Matsakis on 1/30/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "OxDictionaryController.h"
#import "OxNSDictionary.h"
#import "OxNSArray.h"
#import "Ox.h"

@implementation OxDictionaryControllerEntry

@synthesize key = a_key;
@synthesize value = a_value;

- initWithController:(OxDictionaryController*)cont
				 key:(id)key
			   value:(id)value
{
	if ((self = [super init])) {
		m_controller = [cont retain];
		a_key = [key copy]; // NSDictionary's copy their keys
		a_value = [value retain];
	}
	return self;
}

- initWithCoder:(NSCoder *)coder
{
	//if ((self = [super initWithCoder:decoder])) {
	if ((self = [super init])) {
		m_controller = [[coder decodeObjectForKey:@"OxController"] retain];
		a_key = [[coder decodeObjectForKey:@"OxKey"] retain];
		a_value = [[coder decodeObjectForKey:@"OxValue"] retain];
	}
	return self;
}

- (void) dealloc
{
	[a_key release];
	[a_value release];
	[super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)coder
{
	//[super encodeWithCoder:coder];
	[coder encodeObject:m_controller forKey:@"OxController"];
	[coder encodeObject:self.key forKey:@"OxKey"];
	[coder encodeObject:self.value forKey:@"OxValue"];
}

- (void) setKey:(id)key
{
	// Changing the key: make sure that there is no other entry with the same key
	NSDictionary *dictionary = m_controller.dictionary;
	if ([dictionary objectForKey:key])
		return;
	
	// Update our key.  This way when we call setDictionary: below,
	// we will re-use the same entry.
	id oldKey = a_key;
	a_key = [key copy]; // NSDictionary's copy their keys
	
	NSDictionary *newDictionary = [dictionary dictionaryWithKey:key replacingKey:oldKey];
	m_controller.dictionary = newDictionary;
	[oldKey release];
}

- (void) setValue:(id)value
{
	// Changing the value: no need for a safety check, key is still unique!
	NSDictionary *dictionary = m_controller.dictionary;
	
	[a_value release];
	a_value = [value retain];
	
	NSDictionary *newDictionary = [dictionary dictionaryWithValue:value forKey:self.key];
	m_controller.dictionary = newDictionary;
}

- (void) internallySetValue:(id)value
{
	[self willChangeValueForKey:@"value"];
	[a_value release];
	a_value = [value retain];
	[self didChangeValueForKey:@"value"];
}

@end

@implementation OxDictionaryController

+ (void) initialize
{
	[self exposeBinding:@"dictionary"];
	[self exposeBinding:@"sortSelector"];
	[self exposeBinding:@"selectionIndexes"];
}

@synthesize dictionary = a_dictionary;
@synthesize entries = a_entries;
@synthesize sortSelector = a_sortSelector;
@synthesize selectionIndexes = a_selectionIndexes;

- (void) internallySetEntries:(NSArray*)entries
{
	[self willChangeValueForKey:@"entries"];
	[a_entries autorelease];
	a_entries = [entries retain];
	[self didChangeValueForKey:@"entries"];	
}

- init
{
	if ((self = [super init])) {
		a_dictionary = [[NSDictionary dictionary] retain];
		a_entries = [[NSArray array] retain];
		a_sortSelector = @selector(compare:);
		a_selectionIndexes = [[NSIndexSet indexSet] retain];
	}
	return self;
}

- initWithCoder:(NSCoder *)coder
{
	if ((self = [super initWithCoder:coder])) {
		// Is it necessary to announce that we will change value for key?  I don't THINK so.
		a_sortSelector = sel_registerName([[coder decodeObjectForKey:@"OxSortSelector"] UTF8String]);
		a_entries = [[coder decodeObjectForKey:@"OxEntries"] retain];
		a_dictionary = [[coder decodeObjectForKey:@"OxDictionary"] retain];
		a_selectionIndexes = [[coder decodeObjectForKey:@"OxSelectionIndexes"] retain];
	}
	return self;
}

- (void) dealloc
{
	[a_dictionary release];
	[a_entries release];
	[a_selectionIndexes release];
	[super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)coder
{
	[super encodeWithCoder:coder];
	[coder encodeObject:[NSString stringWithUTF8String:sel_getName(a_sortSelector)] forKey:@"OxSortSelector"];
	[coder encodeObject:self.entries forKey:@"OxEntries"];
	[coder encodeObject:self.dictionary forKey:@"OxDictionary"];
	[coder encodeObject:self.selectionIndexes forKey:@"OxSelectionIndixes"];
}

- (void) adjustEntriesForNewDictionary:(NSDictionary*)newDictionary
{
	// Find the current entries: resort them because one of their keys may have changed,
	// triggering a new dictionary.
	NSArray *curEntries = [self.entries sortedArrayUsingKey:@"key" ascending:YES compareSelector:self.sortSelector];
	
	// Find the new value for the dictionary and get a sorted list of its keys.
	NSArray *newKeys = [[newDictionary allKeys] sortedArrayUsingSelector:self.sortSelector];
	
	// Go through the keys one-by-one and synchronize with the entries array.
	NSMutableArray *newEntries = [NSMutableArray array];
	int newI = 0, curI = 0, newC = [newKeys count], curC = [curEntries count];
	while (newI < newC && curI < curC) {
		OxDictionaryControllerEntry *curEntry = [curEntries objectAtIndex:curI];
		id newKey = [newKeys objectAtIndex:newI];
		id curKey = curEntry.key;
		
		NSComparisonResult cmp = (NSComparisonResult)[curKey performSelector:self.sortSelector withObject:newKey];
		
		if (cmp == NSOrderedSame && [newKey isEqual:curKey]) {
			id newValue = [newDictionary objectForKey:newKey];
			id curValue = curEntry.value;
		
			if (![newValue isEqual:curValue])
				[curEntry internallySetValue:newValue];
			
			newI++;
			curI++;
			[newEntries addObject:curEntry];
		} else if (cmp == NSOrderedAscending) { // curKey < newKey
			// We ordered in an ascending fashion.  If curKey was in the new dictionary,
			// we would have seen it!  Therefore curKey was deleted.
			curI++; // skip it
		} else { // newKey < curKey			
			id newValue = [newDictionary objectForKey:newKey];
			OxDictionaryControllerEntry *entry = [[OxDictionaryControllerEntry alloc] initWithController:self key:newKey value:newValue];
			[newEntries addObject:entry];
			[entry release];
			newI++;
		}
	}
	
	// Entries added at the end.
	for (; newI < newC; newI++) {
		id newKey = [newKeys objectAtIndex:newI];
		id newValue = [newDictionary objectForKey:newKey];
		OxDictionaryControllerEntry *entry = [[OxDictionaryControllerEntry alloc] initWithController:self key:newKey value:newValue];
		[newEntries addObject:entry];
		[entry release];
	}
	
	// Update entries, but only if we added or removed anything.
	if (![newEntries isEqual:self.entries])
		[self internallySetEntries:newEntries];
}

- (void) setDictionary:(NSDictionary*)dictionary
{
	[a_dictionary autorelease];
	a_dictionary = [dictionary copy];
	
	[self adjustEntriesForNewDictionary:a_dictionary];
}

#pragma mark -
#pragma mark Entry Add, Remove, etc

- (BOOL) canAdd
{
	return YES;
}

+ (NSSet*) keyPathsForValuesAffectingCanRemove
{
	return OxSet(@"selectionIndexes");
}

- (BOOL) canRemove
{
	return [self.selectionIndexes count] != 0;
}

- (void) addKey:(id)key value:(id)value
{
	NSDictionary *dictionary = self.dictionary;

	NSDictionary *newDictionary;
	if (dictionary)
		newDictionary = [dictionary dictionaryWithValue:value forKey:key];
	else
		newDictionary = [NSDictionary dictionaryWithObject:value forKey:key];
	
	self.dictionary = newDictionary;
}

- (IBAction) addEntry:(id)sender
{
	// Create a unique key.
	for (int i = 0; i < INT_MAX; i++) {
		NSString *newKey = OxFmt(@"New Key %d", i);
		if ([self.dictionary objectForKey:newKey] == nil) {
			[self addKey:newKey value:@"New Value"];
			return;
		}
	}
}

- (IBAction) removeSelectedEntries:(id)sender
{
	NSArray *deadKeys = [[self.entries objectsAtIndexes:self.selectionIndexes] valueForKey:@"key"];
	if ([deadKeys count] > 0) {
		NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:self.dictionary];
		for (id key in deadKeys)
			[dict removeObjectForKey:key];
		self.dictionary = dict;
	}
}

@end
