//
//  EXPAssignmentElement.m
//  dde
//
//  Created by Ashley on 21/07/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "EXPElement.h"
#import "EXPAssignmentOP.h"
#import "EXPDimensionElement.h"
#import "EXPDataElement.h"
#import "EXPConstant.h"
//#import "EXPAssemblerConstants.h"
#import "EXPTernOp.h"
#import "EXPVirtualMachine.h"
#import "EXPSymbolReference.h"
#import "EXPError.h"

/*void compileIndexLoopHeader(EXPVirtualMachine *machine, int nIndices, NSArray *indices, unsigned int *branchAddresses, unsigned int *loopAddresses)

{
	int i;
	for(i=0; i<nIndices; i++) {
		EXPDimensionElement *element = [indices objectAtIndex:i];
//			NSLog(@"%@", element);
		[machine putOpcode:LDA mode:IMM+INTEGER];
		[machine putUInt:0];
		[machine putOpcode:STA mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		loopAddresses[i] = [machine ptr];
		[machine putOpcode:LDA mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		[machine putOpcode:GTH mode:IMM+INTEGER];
		[machine putUInt:[element upperBound]];
		[machine putOpcode:BNE mode:LONG];
		branchAddresses[i] = [machine ptr];
		[machine putUInt:0];
	}
		
}

void compileIndexLoopFooter(EXPVirtualMachine *machine, int nIndices, NSArray *indices, unsigned int *branchAddresses, unsigned int *loopAddresses)
{
	int i;
	for(i=nIndices-1; i>=0; i--) {
		EXPDimensionElement *element = [indices objectAtIndex:i];
		[machine putOpcode:INC mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		[machine putOpcode:BRA mode:LONG];
		[machine putUInt:loopAddresses[i]];
	}

	unsigned int here = [machine ptr];
	for(i=0; i<[indices count]; i++) {
		[machine putUInt:here at:branchAddresses[i]];
	}

} */

// Given two statements, which should be executed first?
int EXPAssignmentSortFunction(EXPAssignmentOp *assignment1, EXPAssignmentOp *assignment2, void *context)
{
//	printf("Comparing:\n %s \nand \n%s\n", [[assignment1 description] UTF8String], [[assignment2 description] UTF8String]);
	
	NSComparisonResult res;
	NSDictionary *lookup = (NSDictionary *)context;
	
	if ([assignment1 dependsUpon:assignment2 withLookup:lookup]) {
//			res = NSOrderedAscending;
//			printf("NSOrderedAscending\n\n");
		res = NSOrderedDescending;
//		printf("NSOrderedDescending\n\n");
	} else if ([assignment2 dependsUpon:assignment1 withLookup:lookup]) {
		res = NSOrderedAscending;
//		printf("NSOrderedAscending\n\n");
//			res = NSOrderedDescending;
//			printf("NSOrderedDescending\n\n");
	} else {
		res = NSOrderedSame;
//		printf("NSOrderedSame\n\n");
	}
	
	return res;
}

int EXPAssignmentSortFunction2(void *context, EXPAssignmentOp **assignment1, EXPAssignmentOp **assignment2)
{
//	printf("Comparing:\n %s \nand \n%s\n", [[assignment1 description] UTF8String], [[assignment2 description] UTF8String]);
	
	NSComparisonResult res;
	NSDictionary *lookup = (NSDictionary *)context;
	
	if ([*assignment1 dependsUpon:*assignment2 withLookup:lookup]) {
//			res = NSOrderedAscending;
//			printf("NSOrderedAscending\n\n");
		res = NSOrderedDescending;
//		printf("NSOrderedDescending\n\n");
	} else if ([*assignment2 dependsUpon:*assignment1 withLookup:lookup]) {
		res = NSOrderedAscending;
//		printf("NSOrderedAscending\n\n");
//			res = NSOrderedDescending;
//			printf("NSOrderedDescending\n\n");
	} else {
		res = NSOrderedSame;
//		printf("NSOrderedSame\n\n");
	}
	
	return res;
}

@implementation EXPAssignmentOp

 - (id) initWithLeft:(id)left right:(id)right// primes:(int)primes
{
	
	self = [super initOp:@"=" withLeft:left AndRight:right];
	if (self!=nil) {
//		_primes = primes;
		_indices = [[NSMutableArray alloc] init];
		_dependencies = [[NSMutableArray alloc] init];
		[self storeDependencies];
	}
	
	return self;
} 

- (void) setIsInitialiser:(BOOL)isInitialiser
{
	_isInitialiser = isInitialiser;
}

- (BOOL) isInitialiser
{
	return _isInitialiser;
}

/*- (void) setRight:(id)right
{
	NSLog(@"Class = %@", [self class]);
	[right retain];
	[_rightHandSideList release];
	_rightHandSideList = right;
}

- (id) right
{
	return _rightHandSideList;
} */

- (void) addRight:(id)right
{
	[[self right] addObject:right];
	[self addDependency:right];
}

- (void) setIndices:(id)indices
{
	[indices retain];
	[_indices release];
	_indices = indices;
}

- (id) indices
{
	return _indices;
}

- (void) setDependencies:(id)dependencies
{
	[dependencies retain];
	[_dependencies release];
	_dependencies = dependencies;
}

- (id) dependencies
{
	return _dependencies;
}

- (void) addDependency:(EXPExpression *)exp
{
//	[[self dependencies] addObject:dependencies];
	if ([exp isMemberOfClass:[EXPTernOp class]]) {
		EXPTernOp *op = (EXPTernOp *)exp;
		[self addDependency:[op left]];
		[self addDependency:[op right]];
		[self addDependency:[op third]];
	} else if ([exp isMemberOfClass:[EXPBinOp class]]) {
		EXPBinOp *op = (EXPBinOp *)exp;
		[self addDependency:[op left]];
		[self addDependency:[op right]];
	} else if ([exp isMemberOfClass:[EXPUnaryOp class]]) {
		EXPUnaryOp *op = (EXPBinOp *)exp;
		[self addDependency:[op left]];
	} else if ([exp isMemberOfClass:[EXPSymbolReference class]]) {
		EXPSymbolReference *ref = (EXPSymbolReference *)exp;
		EXPDataElement *aux = [ref element];
//		printf("%s\n", [[aux description] UTF8String]);
		NSMutableArray *dependencies = [self dependencies];
		NSString *type = [aux elementType];
		if ([type isEqualToString:@"auxiliary"] || [type isEqualToString:@"variable"] || [type isEqualToString:@"parameter"]) {
			if (![dependencies containsObject:aux]) {
				[dependencies addObject:aux];
			}
		}
		int i;
		NSArray *arguments = [ref arguments];
		for(i=0; i<[arguments count]; i++) {
			EXPExpression *arg = [arguments objectAtIndex:i];
			[self addDependency:arg];
		}
/*	} else {
		printf("Problem adding dependency!\n"); */
	} 
	
}

- (void) storeDependencies
{
//	NSMutableArray *dependencies = [self dependencies];
	NSArray *right = [self right];
	int i;
	for(i=0; i<[right count]; i++) {
		EXPExpression *exp = [right objectAtIndex:i];
		[self addDependency:exp];
	}
}

/*- (BOOL) dependsUpon:(EXPAuxiliaryElement *)var
{
	NSMutableArray *dependencies = [self dependencies];

	if ([dependencies containsObject:var]) {
		return YES;
	}
	
	int i;
	for(i=0; i<[dependencies count]; i++) {
		EXPAuxiliaryElement *input = [dependencies objectAtIndex:i];
		if (([[input elementType] isEqualToString:@"auxiliary"]) && ([input dependsUpon:var])) {
			return YES;
		}
	}
	
	return NO;
} */

- (BOOL) dependsUpon:(EXPAssignmentOp *)assignment withLookup:(NSDictionary *)lookup
{
//	EXPAuxiliaryElement *right = nil;
	NSMutableArray *dependencies = [self dependencies];
	EXPDataElement *var = [[assignment left] element];
//	NSLog(@"self         = %@", self);
//	NSLog(@"dependencies = %@", dependencies);
//	NSLog(@"var          = %@", var);

	if ([dependencies containsObject:var]) {
		return YES;
	}

	int i;
	for(i=0; i<[dependencies count]; i++) {
		EXPDataElement *input = [dependencies objectAtIndex:i];
		NSString *name = [input name];
		EXPAssignmentOp *dependency = [lookup objectForKey:name];
		if ([dependency dependsUpon:assignment withLookup:lookup]) {
			return YES;
		}
	} 
	
	return NO;
}
/*- (void) storeIndices
{
	NSLog(@"EXPAssignmentOp: storeIndices");
//	BOOL success = YES;
//	Left is always an EXPSymbolReference?
	EXPSymbolReference *leftRef = [self left];
	EXPDataElement *left = [leftRef element];
//	NSLog(@"%@ class = %@", left, [left class]);
	NSArray *dimensions = [left dimensions];
	NSMutableArray *indices = [[NSMutableArray alloc] init];
	BOOL anyIndices = NO;
	
	int i;
	for(i=0; i<[dimensions count]; i++) {
		EXPElement *dimension = [dimensions objectAtIndex:i];
		if ([[dimension elementType] isEqualToString:@"dimension"]) {
			[indices addObject:dimension];
			anyIndices = YES;
		}
	}
	
	if (anyIndices) {
		NSArray *ind = [[NSArray alloc] initWithArray:indices];
		[self setIndices:ind];
		[ind release];
	}
	
	[indices release];
	
	return;
} */
- (void) storeIndices
{
//	NSLog(@"EXPAssignmentOp: storeIndices");
//	[super storeIndices];
	EXPSymbolReference *leftRef = [self left];
	EXPDataElement *left = [leftRef element];
//	NSLog(@"%@ class = %@", left, [left class]);
	NSArray *dimensions = [left dimensions];
	NSMutableArray *indices = [[NSMutableArray alloc] init];
	BOOL anyIndices = NO;
	
	int i;
	for(i=0; i<[dimensions count]; i++) {
		EXPElement *dimension = [dimensions objectAtIndex:i];
		if ([[dimension elementType] isEqualToString:@"dimension"]) {
			[indices addObject:dimension];
			anyIndices = YES;
		}
	}
	
	if (anyIndices) {
		NSArray *ind = [[NSArray alloc] initWithArray:indices];
		[self setIndices:ind];
		[ind release];
	}
	
	[indices release];
	
	return;
} 

- (void) coerce:(EXPError *)error
{
	EXPSymbolReference *left = [self left];

	EXPElement *element = [left element];
//	NSString *leftElementType = [element elementType];
	EXPExpressionType leftType = [element expressionType];
	NSMutableArray *rhs = [self right];
	int i;
	for(i=0; i<[rhs count]; i++) {
		EXPExpression *exp = [rhs objectAtIndex:i];
		EXPExpressionType rightType = [exp expressionType];
		if ((leftType==integerType) && (rightType==doubleType)) {
			NSString *message = [[NSString alloc] initWithFormat:@"Cannot assign double value to integer variable \"%@\".", [[left element] name]];
			[error addError:message atLine:99999/*[parser lineNumber]*/];
			[message release];
		} else if ((leftType==doubleType) && (rightType==integerType)) {
			if ([[exp elementType] isEqualToString:@"constant"]) {
				EXPConstant *constant = (EXPConstant *)exp;
				[constant setExpressionType:doubleType];
			} else {
				EXPUnaryOp *doubleOp = [[EXPUnaryOp alloc] initOp:@"float" withLeft:exp];
				[doubleOp setExpressionType:doubleType];
				[rhs replaceObjectAtIndex:i withObject:doubleOp];
				[doubleOp release];
			}
		}
	}

}

- (NSString *)elementType
{
	return @"assignment";
}

- (NSString *)description
{
	EXPSymbolReference *left = [self left];
/*	NSMutableString *primes = [[NSMutableString alloc] init];
	for(i=0; i<[left primes]; i++) {
		[primes appendString:@"'"];
	} */
	NSArray *right = [self right];
	NSMutableString *rightdesc = [[NSMutableString alloc] initWithString:@"{"];
	int i;
	for(i=0; i<[right count]; i++) {
		EXPExpression *exp = [right objectAtIndex:i];
		[rightdesc appendString:[exp description]];
	}
	[rightdesc appendString:@"}"];
	NSString *desc = [NSString stringWithFormat:@"%@ = %@", [left description], /*primes, */rightdesc];
//	[primes release];
	return desc;
}

- (void) dealloc
{
//	[_indices release];
//	[_rightHandSideList release];
	[_dependencies release];
	[super dealloc];
}

@end