#import "DataSource.h"
#import "ResKnifeResourceProtocol.h"
#import "NSNumber-Range.h"

@implementation DataSource

- (id)init
{
	[self autorelease];
	return nil;
}

- (id)initForType:(NSString *)typeString
{
	self = [super init];
	type = [typeString copy];
	data = [[NSMutableDictionary alloc] init];
	{
		id <ResKnifeResourceProtocol> resource;
		NSArray *resources = [NSClassFromString(@"Resource") allResourcesOfType:type inDocument:nil];	// nil document will search in ANY open document for the correct resource
		NSEnumerator *enumerator = [resources objectEnumerator];
		while( resource = [enumerator nextObject] )
			[data setObject:[resource name] forKey:[resource resID]];
	}
	parsed = [[NSMutableArray alloc] initWithArray:[data allValues]];
	return self;
}

- (void)dealloc
{
	[type release];
	[data release];
	[parsed release];
	[super dealloc];
}

- (NSDictionary *)data
{
	return data;
}

- (void)setData:(NSMutableDictionary *)newData
{
	id old = data;
	data = [newData retain];
	[self parseForString:@"" sorted:YES];
	[old autorelease];
}

- (void)setString:(NSString *)string forResID:(int)resID
{
	[data setObject:string forKey:[NSNumber numberWithInt:resID]];
}

- (void)parseForString:(NSString *)string sorted:(BOOL)sort
{
	// bug: for some reason +[NSNumber isBoundedByRange:] doesn't like a range with a minimum below -30,000 (such as INT16_MIN), so I'm using INT8_MIN and screw everyone using resource IDs below that :P
	[self parseForString:string withinRange:NSMakeRange(INT8_MIN, INT16_MAX) sorted:sort];
}

- (void)parseForString:(NSString *)string withinRange:(NSRange)resIDRange sorted:(BOOL)sort
{
	NSNumber *resID;
	NSString *trimmedString = [DataSource resNameFromStringValue:string];
	NSEnumerator *enumerator = [[data allKeys] objectEnumerator];
	[parsed removeAllObjects];
	if( trimmedString == nil ) trimmedString = @"";
	while( resID = [enumerator nextObject] )
	{
		NSString *value = [data objectForKey:resID];
		NSRange range = [value rangeOfString:trimmedString options:NSCaseInsensitiveSearch];
		if( ((range.location != NSNotFound && range.length != 0) || [trimmedString isEqualToString:@""]) && [resID isBoundedByRange:resIDRange] )
			[parsed addObject:[self stringValueForResID:resID]];
	}
	
	// crap hack to allow user to change the insertion point if what they typed doesn't yet match an existing resource
	if( [parsed count] == 0 ) [parsed addObject:string];
	
	// sort case insensitive if sorting is requested
	if( sort ) [parsed sortUsingSelector:@selector(caseInsensitiveCompare:)];
}

- (id)objectValueForResID:(NSNumber *)resID
{
	return [data objectForKey:resID];
}

- (NSString *)stringValueForResID:(NSNumber *)resID
{
	if( resID && [data objectForKey:resID] )
		return [NSString stringWithFormat:@"%@ {%@}", [data objectForKey:resID], resID];
	else if( [resID shortValue] == -1 )
		return @"";
	else if( resID )
		return [NSString stringWithFormat:@"{%@}", resID];
	return nil;
}

+ (NSNumber *)resIDFromStringValue:(NSString *)string
{
	NSRange span, range = NSMakeRange(0,0);
	span = [string rangeOfString:@"{" options:NSBackwardsSearch];
	if( span.location != NSNotFound )	range.location = span.location +1;
	else return [NSNumber numberWithInt:-1];
	span = [string rangeOfString:@"}" options:NSBackwardsSearch];
	if( span.location != NSNotFound )	range.length = span.location - range.location;
	else return [NSNumber numberWithInt:-1];
	NS_DURING
		NS_VALUERETURN( [[[NSNumber alloc] initWithInt:[[string substringWithRange:range] intValue]] autorelease], NSNumber* );
	NS_HANDLER
		NS_VALUERETURN( [NSNumber numberWithInt:-1], NSNumber* );
	NS_ENDHANDLER
}

+ (NSString *)resNameFromStringValue:(NSString *)string
{
	NSRange range = [string rangeOfString:@"{" options:NSBackwardsSearch];
	if( range.location != NSNotFound && range.location > 0 )
		return [string substringToIndex:range.location -1];
	else if( range.location == 0 )
		return nil;
	else return string;
}

/* NSComboBox Informal Prototype Implementation */

- (id)comboBox:(NSComboBox *)comboBox objectValueForItemAtIndex:(int)index
{
	return [parsed objectAtIndex:index];
}

- (int)numberOfItemsInComboBox:(NSComboBox *)comboBox
{
	return [parsed count];
}

/* Combo Box Delegate Methods */

- (BOOL)control:(NSControl *)control isValidObject:(id)object
{
	return [parsed containsObject:object];
}

/* Description */

- (NSString *)description
{
	return [NSString stringWithFormat:@"\nType: %@\nData: %@\nParsed Data: %@\n", type, [data description], [parsed description]];
}

@end
