//
//  EXPDDESolveGenerator.m
//  dde
//
//  Created by ashley on 15/08/2008.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "EXPDDESolveGenerator.h"
#import "EXPExpression.h"
#import "EXPUnaryOp.h"
#import "EXPBinOp.h"
#import "EXPTernOp.h"
#import "EXPConstant.h"
#import "EXPParameterElement.h"
#import "EXPAuxiliaryElement.h"
#import "EXPStateElement.h"
#import "EXPFunctionElement.h"
#import "EXPAssignmentOp.h"
#import "EXPSymbolReference.h"
#import "EXPTableElement.h"
#import "EXPBlockElement.h"
#import "EXPSwitchElement.h"

@implementation EXPDDESolveGenerator

- (id) init
{
	if ((self = [super init])!=nil) {
		_package = @"ddesolve";
	}
	return self;
}

- (void) setPackage:(id)package
{
	[package retain];
	[_package release];
	_package = package;
}

- (id) package
{
	return _package;
}

- (BOOL) generate:(EXPBlockElement *)block /*solv95:(BOOL)solv95*/ error:(EXPError *)error
{
	BOOL success = YES;
	NSString *package = [self package];

	[self outLine:@"#include <math.h>" indentationLevel:0];
	[self outLine:@"#include <stdio.h>" indentationLevel:0];
	[self outLine:@"#include <stdlib.h>" indentationLevel:0];
	[self outLine:@"#include <string.h>" indentationLevel:0];
	if ([package isEqualToString:@"solv95"]) {
		[self outLine:@"#include <windows.h>" indentationLevel:0];
	}
	[self outLine:@"#include <dde.h>\n" indentationLevel:0];
	
	NSArray *parameters = [block parameters];
	NSArray *variables = [block variables];
	NSArray *switches = [block switches];
	NSArray *auxiliaries = [block auxiliaries];
	NSArray *initialisers = [block initialisers];
	NSArray *statements = [block statements];

	int nConstants = [parameters count];
	int nVariables = [variables count];
	int nAuxiliaries = [auxiliaries count];
	int nSwitches = [switches count];

	int nextAddress = 0;
	int nDelayVariables = 0;
	int i;
	for(i=0; i<nVariables; i++) {
		EXPStateElement *variable = [variables objectAtIndex:i];
		[variable setAddress:nextAddress];
		if ([variable isDelayVariable]) {
			[variable setDelayNumber:nDelayVariables];
			nDelayVariables++;
		}
		nextAddress += [variable numberOfElements];
	}

// Decalare auxiliaries
	nextAddress = 0;
	for(i=0; i<nAuxiliaries; i++) {
		EXPAuxiliaryElement *auxiliary = [auxiliaries objectAtIndex:i];
		[auxiliary setAddress:nextAddress];
		nextAddress += [auxiliary numberOfElements];
		[self outLine:[NSString stringWithFormat:@"double _auxiliary%d;", i] indentationLevel:0];
	} 

//	Declare table functions as globals.
	for(i=0; i<[initialisers count]; i++) {
		EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
		EXPElement *left = [exp left];
		NSString *elementType = [left elementType];
		if ([elementType isEqualToString:@"table"]) {
			NSString *name = [left name];
			[self outLine:[NSString stringWithFormat:@"interp *%@;", [self tableVariableForName:name]] indentationLevel:0];
		}
	}
	[self outLine:@"" indentationLevel:0];

	nextAddress = 0;
	for(i=0; i<nConstants; i++) {
		EXPParameterElement *parameter = [parameters objectAtIndex:i];
		[parameter setAddress:nextAddress];
		nextAddress += [parameter numberOfElements];
	} 

	if (nDelayVariables>0) {
		[self outLine:@"double pastvaluewithinitvalue(int num, double tdelay, int m, double initValue, double (*pastvalue)())" indentationLevel:0];
		[self outLine:@"{ " indentationLevel:0];
		[self outLine:@"double val;" indentationLevel:1];
		[self outLine:@"if (tdelay>0) {" indentationLevel:1];
		[self outLine:@"val = pastvalue(num, tdelay, m);" indentationLevel:2];
		[self outLine:@"} else {" indentationLevel:1];
		[self outLine:@"val = initValue;" indentationLevel:2];
		[self outLine:@"}" indentationLevel:1];
		[self outLine:@"}" indentationLevel:0];
	}
//	NSLog(@"%@", self);

	[self outLine:@"void initcons(int *no_vars, int *no_cons)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];
	[self outLine:[NSString stringWithFormat:@"*no_cons = %d;", nConstants] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"*no_vars = %d;", nVariables] indentationLevel:1];
	[self outLine:@"}\n" indentationLevel:0];
	
	[self outLine:@"void switchfunctions(double *sw, double *s, double *c, double t)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];
	for(i=0; i<nSwitches; i++) {
		[self indent:1];
		EXPSwitchElement *switcher = [switches objectAtIndex:i];
		EXPExpression *exp = [switcher header];
		[self outString:[NSString stringWithFormat:@"sw[%d] = ", i]];
		[self generateExpression:exp error:error];
		[self outString:@";\n"];
	}
	[self outLine:@"}\n" indentationLevel:0];
	
	[self outLine:@"void map(double *s, double *c, double t, int swno)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];
	for(i=0; i<nSwitches; i++) {
		[self indent:1];
		if (i>0) {
			[self outString:@"} else "];
		}
		NSString *string = [[NSString alloc] initWithFormat:@"if (swno==%d) {\n", i];
		[self outString:string];
		[string release];
		EXPSwitchElement *switcher = [switches objectAtIndex:i];
		EXPBlockElement *block = [switcher block];
		[self generateBlock:block error:error];
		[self outLine:@"}\n" indentationLevel:1];
	}
	[self outLine:@"}\n" indentationLevel:0];
	
	if ([package isEqualToString:@"ddesolve"]) {
		[self outLine:@"void grad(double *g, double *s, double *c, double t, double (*pastvalue)())" indentationLevel:0];
	} else {
		[self outLine:@"void grad(double *g, double *s, double *c, double t)" indentationLevel:0];
	}
	[self outLine:@"{ " indentationLevel:0];
	[self setConstantOutputMode:@"normal"];
	for(i=0; i<[statements count]; i++) {
		EXPAssignmentOp *exp = [statements objectAtIndex:i];
		success = [self generateStatement:exp error:error];
	}
	[self outLine:@"}\n" indentationLevel:0];
	
	[self outLine:@"void statescale(double *scale)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];
	[self outLine:@"}\n" indentationLevel:0];
	
	if ([package isEqualToString:@"ddesolve"]) {
		[self outLine:@"void initst(double *s, double *c, double t)" indentationLevel:0];
	} else {
		[self outLine:@"void initstate(double *s, double *c, double t)" indentationLevel:0];
	}
	[self outLine:@"{ " indentationLevel:0];
	for(i=0; i<[initialisers count]; i++) {
		EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
		EXPElement *left = [exp left];
		NSString *elementType = [left elementType];
		if ([elementType isEqualToString:@"table"]) {
			success = [self generateTableDefinition:(EXPTableElement *)[exp left] error:error];
		} else if (![elementType isEqualToString:@"parameter"]) {
			success = [self generateStatement:exp error:error];
		}
	}
	[self outLine:@"}\n" indentationLevel:0];
	
	[self outLine:@"void initout(usercontrol *out)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];

	[self outLine:@"out->no_windows = 0;" indentationLevel:1];

	[self outLine:@"out->xlabel=\"Time\";" indentationLevel:1];

	[self outLine:@"out->fileno = 0;\n" indentationLevel:1];

	[self outLine:[NSString stringWithFormat:@"out->t0 = %@;", [block option:@"tstart"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->t1 = %@;", [block option:@"tstop"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->dt = %@;", [block option:@"tstep"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->tol = %@;", [block option:@"epsilon"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->dout = %@;", [block option:@"outstep"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->hbsize = %@L;", [block option:@"histsize"]] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->nhv = %d;", 1] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->nlag = %d;", nDelayVariables] indentationLevel:1];
	[self outLine:[NSString stringWithFormat:@"out->nsw = %d;\n", [block nSwitches]] indentationLevel:1];
	
//	[self outLine:[NSString stringWithFormat:@"double c[%d];", nConstants] indentationLevel:1];
	
	[self setConstantOutputMode:@"out"];
	for(i=0; i<[initialisers count]; i++) {
		EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
		EXPElement *left = [exp left];
		if ([[left elementType] isEqualToString:@"parameter"]) {
			success = [self generateStatement:exp error:error];
		}
	}
	[self setConstantOutputMode:@"normal"];

	for(i=0; i<nConstants; i++) {
		EXPParameterElement *parameter = [parameters objectAtIndex:i];
		[self outLine:[NSString stringWithFormat:@"out->cname[%d] = \"%@\";", i, [parameter name]] indentationLevel:1];
		NSString *label = [parameter label];
		if ([label length]>0) {
			[self outLine:[NSString stringWithFormat:@"out->cinfo[%d] = \"%@\";", i, label] indentationLevel:1];
		}
	}

	[self outLine:@"}\n" indentationLevel:0];

	[self outLine:@"void storehistory(double *his, double *ghis, double *g, double *s, double *c, double t)" indentationLevel:0];
	[self outLine:@"{ " indentationLevel:0];
	for(i=0; i<nVariables; i++) {
		EXPStateElement *variable = [variables objectAtIndex:i];
		if ([variable isDelayVariable]) {
			int delayNumber = [variable delayNumber];
			NSString *string = [[NSString alloc] initWithFormat:@"his[%d] = s[%d];", delayNumber, delayNumber];
			[self outLine:string indentationLevel:1];
			[string release];
			string = [[NSString alloc] initWithFormat:@"ghis[%d] = g[%d];", delayNumber, delayNumber];
			[self outLine:string indentationLevel:1];
			[string release];
		}
	}
	[self outLine:@"}\n" indentationLevel:0];
	
	if ([package isEqualToString:@"ddesolve"]) {
		[self outLine:@"void washup()" indentationLevel:0];
		[self outLine:@"{ " indentationLevel:0];
		for(i=0; i<[initialisers count]; i++) {
			EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
			EXPElement *left = [exp left];
			NSString *elementType = [left elementType];
			if ([elementType isEqualToString:@"table"]) {
				NSString *name = [left name];
				[self outLine:[NSString stringWithFormat:@"freeInterp(%@);", [self tableVariableForName:name]] indentationLevel:1];
			}
		}
		[self outLine:@"}\n" indentationLevel:0];
	}
	
	return YES;
}

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

@end