//
//  TestParser.m
//  ICeCoffEE
//
//  Created by Nicholas Riley on 6/22/07.
//  Copyright 2007 Nicholas Riley. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "ICeCoffEEParser.h"

#include <stdarg.h>

static void ps(NSString *format, ...) {
    va_list args;
    va_start(args, format);
    NSString *s = [[NSString alloc] initWithFormat: format arguments: args];
    va_end(args);
    printf("%s\n", [s cString]);
    [s release];
}

static void pr(NSString *s, NSRange r) {
    ps([[NSString alloc] initWithFormat: @"%@%@>%@<",
	s, [@"" stringByPaddingToLength: r.location withString: @" " startingAtIndex: 0],
	[@"" stringByPaddingToLength: r.length withString: @"-" startingAtIndex: 0]]);
}

static NSString *tc(NSString *s) {
    NSMutableString *rs = [s mutableCopy];
    [rs replaceOccurrencesOfString: @"\n" withString: @"\\" options: 0 range: NSMakeRange(0, [s length])];
    [rs replaceOccurrencesOfString: @"\t" withString: @">" options: 0 range: NSMakeRange(0, [s length])];
    return [rs autorelease];
}

NSString *ICCF_ErrString(OSStatus err, NSString *context) {    
    return [NSString stringWithFormat: @"%@ (%d)", context, (int)err];
}

void ICCF_CheckRange(NSRange range) {
    if (range.length > 0)
	return;
    
    pr(@"Invalid   ", range);
    @throw([NSException exceptionWithName: NSRangeException
				   reason: @"No URL is selected"
				 userInfo: nil]);
}


// XXX move elsewhere
static NSString *ICCF_StringByRemovingCharactersInSet(NSString *s, NSCharacterSet *set) {
    NSRange range = [s rangeOfCharacterFromSet: set options: 0 range: NSMakeRange(0, [s length])];
    if (range.location == NSNotFound)
	return s;
    
    NSMutableString *ms = [s mutableCopy];
    do {
	[ms deleteCharactersInRange: range];
	range.length = [ms length] - range.location;
        range = [ms rangeOfCharacterFromSet: set options: 0 range: range];
    } while (range.location != NSNotFound);
    
    s = [[ms copy] autorelease];
    [ms release];
    return s;
}

static BOOL check_parse(NSString *uri, NSString *text,
			NSRange delimitedURIRange, NSRange initialSelectionRange) {
    // XXX figure XFAIL-type error checking
    
    NSRange enclosingRange;
    NSString *parsedURI = nil;
    NSException *parseException = nil;
    @try {
	enclosingRange = ICCF_URLEnclosingRange(text, initialSelectionRange);
	// XXX move trimming elsewhere
	parsedURI = ICCF_StringByRemovingCharactersInSet([text substringWithRange: enclosingRange], [NSCharacterSet whitespaceAndNewlineCharacterSet]);
    } @catch (NSException *e) {
	parseException = e;
    }
    
    if ([uri isEqualToString: parsedURI]) {
	return YES;
    }
    
    ps(@"Text       %@", tc(text));
    pr(@"Available ", delimitedURIRange);
    pr(@"Starting  ", initialSelectionRange);
    
    if (parseException != nil) {
	ps(@"Exception  %@", parseException);
    } else {
	pr(@"Parsed    ", enclosingRange);
	ps(@"Parsed     %@", tc(parsedURI));
	ps(@"Desired    %@", tc(uri));
    }
    return NO;
}

CFBundleRef ICCF_bundle = NULL;

// from http://jens.ayton.se/code/files/JANSLogHack.m
extern void _NSSetLogCStringFunction(void (*)(const char *string, unsigned length, BOOL withSyslogBanner));

static void PrintNSLogMessage(const char *string, unsigned length, BOOL withSyslogBanner) {
    puts(string);
}

int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    _NSSetLogCStringFunction(PrintNSLogMessage);
    
    NSArray *a = [NSArray arrayWithContentsOfFile: @"urls.plist"];
    NSCAssert(a != nil, @"Can't get array from urls.plist (wrong cwd? invalid format?)");
    
    ICCF_StartIC();
    
    unsigned pass = 0, fail = 0;
    
    NSEnumerator *e = [a objectEnumerator];
    NSDictionary *d;
    while ( (d = [e nextObject]) != nil) {
	ps(@"");

	NSString *uri = [d objectForKey: @"uri"];
	NSString *text = [d objectForKey: @"text"];
	NSCAssert1(uri != nil, @"No URI in test case description %@", d);
	
	NSRange delimitedURIRange;
	if (text != nil) {
	    NSArray *bits = [text componentsSeparatedByString: @"|"];
	    NSCAssert1([bits count] == 3, @"Sample text is not of the form 'foo|bar|baz': %@", text);
	    text = [bits componentsJoinedByString: @""];
	    delimitedURIRange.location = [(NSString *)[bits objectAtIndex: 0] length];
	    delimitedURIRange.length = [(NSString *)[bits objectAtIndex: 1] length];
	} else {
	    text = uri;
	    delimitedURIRange.location = 0;
	    delimitedURIRange.length = [uri length];
	}
	
	NSArray *bits = [text componentsSeparatedByString: @"^"];
	if ([bits count] == 3) {
	    NSString *joined = [bits componentsJoinedByString: @""];
	    NSRange initialSelectionRange = {
		[(NSString *)[bits objectAtIndex: 0] length],
		[(NSString *)[bits objectAtIndex: 1] length]
	    };
	    delimitedURIRange.length -= 2; // remove ^^
	    if (check_parse(uri == text ? joined : uri, joined,
			    delimitedURIRange, initialSelectionRange)) {
		pass++;
	    } else {
		fail++;
		goto summary;
	    }
	    if (uri == text)
		continue;
	    text = joined;
	}
	
	for (unsigned startOffset = 0 ; startOffset < delimitedURIRange.length ; ++startOffset) {
	    NSRange initialSelectionRange = {
		delimitedURIRange.location + startOffset,
		delimitedURIRange.length != 0
	    };
	    
	    if (check_parse(uri, text, delimitedURIRange, initialSelectionRange)) {
		pass++;
	    } else {
		fail++;
		goto summary;
	    }
	}
    }
    
summary:
    ps(@"PASS %d FAIL %d", pass, fail);
    
    ICCF_StopIC();

    [pool release];
}
