//
//  EXPBinOp.m
//  Expression
//
//  Created by Ashley on 28/02/2006.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#include <math.h>
#import "EXPExpression.h"
#import "EXPUnaryOp.h"
#import "EXPBinOp.h"
#import "EXPSymbolTable.h"
#import "EXPConstant.h"
//#import "EXPAssemblerConstants.h"
#import "EXPVirtualMachine.h"

@implementation EXPBinOp

+ (id) multOpWithLeft:(id)p andRight:(id)pp isMultOp:(BOOL)b 
{
	EXPExpression *op;

	BOOL constant = [p isConstant] && [pp isConstant];
	if (constant) {
		op = [[[EXPConstant alloc] init] autorelease];
		double result;
		if (b) {
			result = [p result] * [pp result];
		} else {
			double denom = [pp result];
			if (denom==0.0) {
				result = 0.0;
			} else {
				result = [p result] / denom;
			}
		}
		[op setValue:result];
	} else {
		if (b) {
			op = [[[EXPBinOp alloc] initOp:@"*" withLeft:p AndRight:pp] autorelease];
		} else {
			op = [[[EXPBinOp alloc] initOp:@"/" withLeft:p AndRight:pp] autorelease];
		}
	}

	return op;
}

+ (id) addOpWithLeft:(id)p andRight:(id)pp isAddOp:(BOOL)b 
{
	EXPExpression *op;
	if ([p isConstant] && [pp isConstant]) {
		op = [[[EXPConstant alloc] init] autorelease];
		double result;
		if (b) {
			result = [p result] + [pp result];
		} else {
			result = [p result] - [pp result];
		}
		[op setValue:result];
	} else {
		if (b) {
			op = [[[EXPBinOp alloc] initOp:@"+" withLeft:p AndRight:pp] autorelease];
		} else {
			op = [[[EXPBinOp alloc] initOp:@"-" withLeft:p AndRight:pp] autorelease];
		}
	}
	
	return op;
}

- (id)initOp:(id)name withLeft:(id)left AndRight:(id) right
{
	[super initOp:name withLeft:left];
	[self setRight:right];
	[self storeIndices];
	return self;
}

- (id) init
{
	return [self initOp:(nil) withLeft:nil  AndRight:nil];
	
}

- (id) right
{
	return _right;
}

- (void)setRight:(id)right
{
	[right retain];
	[_right release];
	_right = right;
	[self storeIndices];
}

- (void) storeIndices
{
	[super storeIndices];
	EXPExpression *right = [self right];
	NSMutableArray *indices = [right indices];
	NSArray *leftIndices = [self indices];
	int i;
	for(i=0; i<[leftIndices count]; i++) {
		id index = [leftIndices objectAtIndex:i];
		if (![indices containsObject:index]) {
			[indices addObject:index];
		}
	}
	
	[self setIndices:indices];
}

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

- (BOOL) isTypeCompatible
{
	EXPExpressionType leftType = [[self left] expressionType];
	EXPExpression *right = [self right];
	EXPExpressionType rightType = [right expressionType];
	BOOL compatible;
	
	if (leftType==integerType) {
//		 compatible = (rightType==integerType) || (rightType==doubleType) || (rightType==indexType);
		 if (rightType==integerType) {
			compatible = YES;
			[self setExpressionType:integerType];
		 } else if (rightType==doubleType) {
			[self setExpressionType:doubleType];
//			[self setRight:[self coerce:doubleType]];
			[self coerceLeft:doubleType];
			compatible = YES;
		 } else if (rightType==indexType) {
			[self setExpressionType:indexType];
			compatible = YES;
		 } else {
			compatible = NO;
		 }
	} else if (leftType==doubleType) {
		 compatible = (rightType==integerType) || (rightType==doubleType);
		[self setExpressionType:doubleType];
		if (rightType==integerType) {
			[self coerceRight:doubleType];
		}
	}  else if (leftType==dimensionType) {
		compatible = NO;
	}  else if (leftType==indexType) {
		compatible = NO;
	}
	
	return compatible;
}

- (void) coerceRight:(EXPExpressionType)newType
{
	EXPExpression *exp = [self right];
	if ([[exp elementType] isEqualToString:@"constant"]) {
		EXPConstant *left = (EXPConstant *)exp;
		[left setExpressionType:newType];
	} else {
		NSString *opName;
		if (newType==doubleType) {
			opName = @"float";
		} else {
			opName = @"int";
		}
		EXPUnaryOp *doubleOp = [[EXPUnaryOp alloc] initOp:opName withLeft:exp];
		[doubleOp setExpressionType:doubleType];
		[self setRight:doubleOp];
		[doubleOp release];
	}
}

- (BOOL) compile:(EXPVirtualMachine *)machine error:(EXPError *)err
{
	return NO;
}

- (double) result
{
	return 0.0;
}

- (NSString *)description
{
	return [NSString stringWithFormat:@"(%@) %@ (%@)", [[self left] description], [self name], [[self right] description]];
}

- (void) dump
{
	[super dump];
	[[self right] dump];
}

- (void) dealloc
{
//	printf("EXPBinOp - (void) dealloc %s\n", [[[self class] description] UTF8String]);
	[_right release];

	[super dealloc];
}

@end