/*
	NSProcessInfo+getopt.m
	Tue Dec 10 11:32:58 CET 2002
 */

#import	"NSProcessInfo+getopt.h"

@implementation NSProcessInfo(Getopt)

static NSDictionary	*optionsDictionaryFromSpecification(NSArray *optionsSpecification)
{
	NSMutableDictionary	*options = [NSMutableDictionary dictionary];
	int	i, count;

	for (i = 0, count = [optionsSpecification count]; i < count; i++)
	{
		NSScanner	*optionScanner = [NSScanner scannerWithString:
			[optionsSpecification objectAtIndex:i]];
		NSString	*option = nil, *optionValueType = nil;

		[optionScanner scanUpToString:@"=" intoString:&option];
		[optionScanner scanString:@"=" intoString:(id *)nil];
		[optionScanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet]
			intoString:&optionValueType];
		[options setObject:optionValueType? optionValueType: @"" forKey:option];
	}
	return options;
}

static NSArray	*_remainingArguments;

- (void)_setRemainingArguments:(NSArray *)args {
	[args retain];
	[_remainingArguments release];
	_remainingArguments = args;
}
- (NSArray *)remainingArguments { return _remainingArguments; }

- (NSDictionary *)getOptionsFromSpecification:(NSArray *)optionsSpecification
	defaults:(NSDictionary *)defaults remainingArguments:(NSArray **)remainingArguments
{
	NSDictionary	*options = optionsDictionaryFromSpecification(optionsSpecification);
	NSMutableDictionary	*optDict = defaults? [defaults mutableCopy]
		: [NSMutableDictionary dictionary];
	NSMutableArray	*remainingArgs = [NSMutableArray array];
	NSArray			*args = [self arguments], *copiedArgs;
	int				i, count;
	NSCharacterSet	*whitespace = [NSCharacterSet whitespaceCharacterSet];

	for (i = 0, count = [args count]; i < count; i++) {
		NSString	*arg = [args objectAtIndex:i];
		// is arg an option?
		if ([@"--" isEqualToString:[arg substringWithRange:NSMakeRange(0, 2)]]) {
			NSString	*option = [arg substringWithRange:NSMakeRange(2, [arg length] - 2)];
			NSScanner	*optionScanner = [NSScanner scannerWithString:option];
			NSString	*optionName;
			id			optionValue;

			[optionScanner scanUpToString:@"=" intoString:&optionName];
			if (! [options objectForKey:optionName]) {
				[NSException raise:NSInvalidArgumentException
					format:@"option %@ not known", optionName];
			}

			if (![[options valueForKey:optionName] isEqualToString:@""]) {
				// option requires argument
				if ([optionScanner scanString:@"=" intoString:(id *)nil]) {
					// the option value is given by a "="
					[optionScanner scanUpToCharactersFromSet:whitespace	intoString:&optionValue];
				} else {
					// the next argument is the option value
					if (i == count - 1)
						[NSException raise:NSInvalidArgumentException
							format:@"option %@ requires an argument", optionName];

					optionValue = [args objectAtIndex:++i];
					// <i> type checking
				}
			} else {	// encountered a switch
				if ([optionScanner scanString:@"=" intoString:(id *)nil]) {
					// we expect either YES or NO
					[optionScanner scanUpToCharactersFromSet:whitespace	intoString:&optionValue];
					if ([optionValue isEqualToString:@"YES"])
						optionValue = [NSNumber numberWithInt:1];
					else if ([optionValue isEqualToString:@"NO"])
						optionValue = [NSNumber numberWithInt:0];
					else
						[NSException raise:NSInvalidArgumentException
							format:@"argument for switch must be either 'YES' or 'NO' "
								@"for option %@", optionName];

				} else optionValue = [NSNumber numberWithInt:1];
			}
			[optDict setObject:optionValue forKey:optionName];
		} else {
			[remainingArgs addObject:arg];
		}
	}

	copiedArgs = [[remainingArgs copy] autorelease];
	[self _setRemainingArguments:copiedArgs];
	if (remainingArguments) *remainingArguments = copiedArgs;
	return [[optDict copy] autorelease];
}

- (NSDictionary *)getOptionsFromSpecification:(NSArray *)optionsSpecification defaults:(NSDictionary *)defaults
{
	return [self getOptionsFromSpecification:optionsSpecification defaults:defaults
		remainingArguments:NULL];
}

- (NSDictionary *)getOptionsFromSpecification:(NSArray *)optionsSpecification
{
	return [self getOptionsFromSpecification:optionsSpecification defaults:nil];
}

@end
