#import <Foundation/Foundation.h>

#include <math.h>
#import "DDERungeKutta23.h"
#import "EXPElement.h"
#import "EXPSymbolReference.h"
#import "EXPVirtualMachine.h"
#import "EXPParser.h"
#import "EXPError.h"
#import "EXPModel.h"
#import "EXPDDEModelLoader.h"
#import "EXPBlockElement.h"
#import "EXPMachineGenerator.h"
#import "EXPCCodeGenerator.h"
#import "PLTMatrix.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSProcessInfo *processInfo = [NSProcessInfo processInfo];
	NSArray *arguments = [processInfo arguments];
	NSString *path;
	path = @"/Users/ashley/Projects/Objective-C/DDESolver/MathsProgs/dde/test.dde";
	BOOL echo = NO;
	BOOL convert = NO;
	BOOL debugMode = NO;
	BOOL cgiMode = NO;
	int nMemoryBlocks = 64;
	NSMutableDictionary *definitions = [[NSMutableDictionary alloc] init];
	NSString *outFileName = @"";
	NSString *outFileType = @"hex";
	
	int narg;
	for (narg = 1; narg<[arguments count]; narg++) {
		NSString *arg = [arguments objectAtIndex:narg];
		int arglen = [arg length];
		if ([arg characterAtIndex:0]=='-') {
			if ([arg isEqualToString:@"-"]) {
				break;
			}
			NSRange range = NSMakeRange(2, arglen-2);
			NSString *def = [arg substringWithRange:range];
			char optChar = [arg characterAtIndex:1];
			if (optChar=='v') {
				echo = YES;
			} else if (optChar=='g' || optChar=='d') {
				debugMode = YES;
			} else if (optChar=='F') {
				outFileType = def;
			} else if (optChar=='D') {
				NSArray *comps = [def componentsSeparatedByString:@"="];
				if ([comps count]==2) {
					[definitions setValue:[comps objectAtIndex:1] forKey:[comps objectAtIndex:0]];
				}
			} else if (optChar=='c') {
				if ([def length]>0) {
					outFileName = def;
					convert = YES;
				}
			} else {
			}
		} else {
			path = arg;
		}
	}
	
	if (cgiMode) {
		NSProcessInfo *processInfo = [NSProcessInfo processInfo];
		NSDictionary *environment = [processInfo environment];
		NSString *queryString = [environment objectForKey:@"QUERY_STRING"];
		if (queryString!=nil) {
		}
	}

#ifdef GNUSTEP
	NSString *sourceFileContents = [[NSString alloc] initWithContentsOfFile:path];
#else
	NSString *sourceFileContents = [[NSString alloc] initWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil];
#endif	

	if (sourceFileContents==nil) {
		printf("Cannot open input file\n");
		exit(-1);
	}
	
	EXPError *error = [[EXPError alloc] init];
	
	EXPModel *model = [[EXPModel alloc] initWithMemory:8096*nMemoryBlocks];
	EXPDDEModelLoader *loader = [[EXPDDEModelLoader alloc] initWithString:sourceFileContents withEcho:echo error:error];
	[sourceFileContents release];

	EXPBlockElement *block = [[EXPBlockElement alloc] initWithEnclosingBlock:nil];
	[block setModel:model];
	[block  setName:@"MAIN"];
	BOOL success = [loader loadModel:block];
	if (!success || ([error numErrors]!=0)) {
		printf("Syntax error in model\n");
		exit(-1);
	}
	
	if (debugMode) {
		[block list];
	} 
	[loader release];
	[model setBlock:block];
	
	if (echo) printf("\n");
	
//	EXPVirtualMachine *machine = [[EXPVirtualMachine alloc] initWithMemory:8096*nMemoryBlocks];
	[model setEcho:echo];
	EXPMachineGenerator *generator = [[EXPMachineGenerator alloc] init];
	success = [generator generate:block machine:model error:error];
	[generator release];

	if(!success || ([error numErrors]!=0)) {
		printf("Model failed to compile.\n");
//		[error reportErrors];
		exit(-1);
	}
	
	NSDictionary *options = [block options];
	NSEnumerator *enumerator = [options keyEnumerator];
	id key;
 
	while ((key = [enumerator nextObject])) {
		id option = [block option:key];
		[model setOption:option forName:key];
	}	

	if (convert) {
		if ([outFileType isEqualToString:@"hex"]) {
			[model writeToFile:outFileName];
		} else if ([outFileType isEqualToString:@"Csolv95"]) {
			EXPCCodeGenerator *generator = [[EXPCCodeGenerator alloc] init];
			[generator generate:block toFile:outFileName solv95:NO error:error];
			[generator release];
		} else {
			printf("Invalid output file type.\n");
		}
	} else {
		int top = [model ptr];

		[model setGp:top];
		[model setSp:[model dataTop] + top];

		[model initConstants];
		[model setConstants:definitions];
	
		int nVariables = [model nVariables];
		double *state = (double *)calloc(nVariables, sizeof(double));
		double *gradients = (double *)calloc(nVariables, sizeof(double));
		[definitions release];
	
		DDERungeKutta23 *solver = [[DDERungeKutta23 alloc] initWithRhs:model];
	
		[model setHistory:solver];
		
		int exitCode = [model exitCode];
		if (exitCode==0) {
			success = [solver solve];
			if (!success) {
				printf("Solver returned with errors...\n");
			}
	
			if (debugMode) {
				printf("Steps accepted = %d\n", [solver accepted]);
				printf("Steps rejected = %d\n", [solver rejected]);
			}

			[model print];
		}

		[model setPc:[model washupExecutionAddress]];
		[model runFromPc];
		[model setHistory:nil];
		[solver release]; 
		free(state);
		free(gradients);
	}
	
	[block release];
	[error release];
	[model release];
	[pool release];

	if (debugMode) {
		[EXPElement reportElementCount];
		[EXPExpression reportElementCount];
	}
	
    return 0;
}