//
//  EXPFlowComponent.m
//  Expression
//
//  Created by ashley on 16/11/2008.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "EXPFlowComponent.h"
#import "EXPStockComponent.h"
#import "EXPSymbolTable.h"
#import "EXPModelObject.h"

@implementation EXPFlowComponent

- (id) componentType
{
	return @"Flow";
}

- (void) setFromElement:(NSXMLElement *)element
{
	[super setFromElement:element];
	NSXMLElement *startElement = [self getElementChild:@"start" forElement:element];
	if (startElement!=nil) {
		[self setStart:[startElement stringValue]];
	}
	
	NSXMLElement *endElement = [self getElementChild:@"end" forElement:element];
	if (endElement!=nil) {
		[self setEnd:[endElement stringValue]];
	}
	
}

- (void)  putToElement:(NSXMLElement *)element
{
	[super putToElement:element];
	[self putString:[[self start] name] toElement:element named:@"start"];
	[self putString:[[self end] name] toElement:element named:@"end"];
}

- (void) setStart:(id)start
{
	[start retain];
	[_start release];
	_start = start;
}

- (id) start
{
	return _start;
}

- (void) setEnd:(id)end
{
	[end retain];
	[_end release];
	_end = end;
}

- (id) end
{
	return _end;
}

- (BOOL) acceptsInputs
{
	return YES;
}

- (BOOL) acceptsOutputs
{
	return YES;
}

- (BOOL) process:(id)symbolTable
{
	BOOL success = [super process:symbolTable];
	
	NSString *startName = [self start];
	if (startName!=nil) {
		EXPStockComponent *startStock = [symbolTable symbolForName:startName];
		[self setStart:startStock];
		if (startStock==nil) {
		} else {
			if ([[self componentType] isEqualToString:@"Flow"]) {
				NSMutableArray *outflows = [startStock outflows];
				[outflows addObject:self];
			}
		}
//		[connectedComponents addObject:startStock];
	}
	NSString *endName = [self end];
	if (endName!=nil) {
		EXPStockComponent *endStock = [symbolTable symbolForName:endName];
		[self setEnd:endStock];
		if (endStock==nil) {
		} else {
			if ([[self componentType] isEqualToString:@"Flow"]) {
				NSMutableArray *inflows = [endStock inflows];
				[inflows addObject:self];
			}
		}
//		[connectedComponents addObject:endStock];
	} 

	return success;
}

- (NSPoint) normalVectorForPoint:(NSPoint)point length:(float)length
{
	NSPoint centre = [self valvePosition];
	
	float xp = point.x;
	float yp = point.y;

//	NSSize size = [self size];
//	float w = size.width;
//	float radius = w/2.0;

	float xc = centre.x;
	float yc = centre.y;

	float nx = xp - xc;
	float ny = yp - yc;
	float nlength = hypot(nx, ny);
	
	return NSMakePoint(length*nx/nlength, length*ny/nlength);
}

- (BOOL) controlAreaContainsPoint:(NSPoint)point
{
	NSBezierPath *path;
	NSPoint pos = [self pos];
	NSPoint pos2 = [self pos2];
	NSPoint controlVector2 = [self controlVector2];
	NSPoint controlVector = [self controlVector];
	NSPoint controlPoint1 = NSMakePoint(pos.x + controlVector.x,  pos.y + controlVector.y);
	NSPoint controlPoint2 = NSMakePoint(pos2.x - controlVector2.x,  pos2.y - controlVector2.y);
	EXPModelObject *modelObject = [self modelObject];
	NSDictionary *defaults = [modelObject defaults];
	float controlSquareSize = [[defaults objectForKey:@"ControlSquareSize"] floatValue];
	
	NSRect rect1 = NSMakeRect(pos.x - controlSquareSize/2.0, pos.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize);
	if (NSPointInRect(point, rect1)) {
		[self setControlPointSelected:3];
		return YES;
	}
	
	NSRect rect2 = NSMakeRect(pos2.x - controlSquareSize/2.0, pos2.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize);
	if (NSPointInRect(point, rect2)) {
		[self setControlPointSelected:4];
		return YES;
	}

	path = [NSBezierPath bezierPathWithOvalInRect:
		NSMakeRect(controlPoint1.x - controlSquareSize/2.0, controlPoint1.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize)];
	if ([path containsPoint:point]) {
		[self setControlPointSelected:1];
		return YES;
	}

	path = [NSBezierPath bezierPathWithOvalInRect:
		NSMakeRect(controlPoint2.x - controlSquareSize/2.0, controlPoint2.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize)];
	if ([path containsPoint:point]) {
		[self setControlPointSelected:2];
		return YES;
	}

	[self setControlPointSelected:0];
	return NO;
}

/*- (int) controlPointSelected
{
	return 0;
} */

- (void) moveBy:(NSPoint)change moveStart:(BOOL)moveStart
{
	NSPoint oldValvePosition = [self valvePosition];
	if (moveStart) {
		NSPoint pos = [self pos];
		[self setPosition:NSMakePoint(pos.x + change.x, pos.y + change.y)];
	} else {
		NSPoint pos2 = [self pos2];
		[self setPosition2:NSMakePoint(pos2.x + change.x, pos2.y + change.y)];
	}
	NSPoint valvePosition = [self valvePosition];

	NSSet *selection = [[self modelObject] selection];
	NSArray *inputs = [self inputs];
	int i;
	for(i=0; i<[inputs count]; i++) {
		EXPComponent *input = [inputs objectAtIndex:i];
		if (![selection containsObject:input]) {
			NSPoint pos2 = [input pos2];
			[input setPosition2:NSMakePoint(pos2.x + valvePosition.x - oldValvePosition.x, pos2.y + valvePosition.y - oldValvePosition.y)];
		}
	}

}

- (void) moveComponentTo:(NSPoint)point inView:(id)view
{
/*	NSPoint pos = [self pos];
	[self setPosition:NSMakePoint(pos.x + change.x, pos.y + change.y)];
	NSPoint pos2 = [self pos2];
	[self setPosition2:NSMakePoint(pos2.x + change.x, pos2.y + change.y)]; */

	int controlPoint = [self controlPointSelected];
	if (controlPoint==3) {
		printf("Got control point %d\n", controlPoint);
//		EXPFlowComponent *component = (EXPFlowComponent *)self;
		EXPComponent *startComponent = [self start];
		NSPoint nearestPoint = [startComponent closestSurfacePointToPoint:point];
		[self setPosition:nearestPoint];
	} else if (controlPoint==4) {
		printf("Got control point %d\n", controlPoint);
//		EXPFlowComponent *component = (EXPFlowComponent *)self;
		EXPComponent *endComponent = [self end];
		NSPoint nearestPoint = [endComponent closestSurfacePointToPoint:point];
		[self setPosition2:nearestPoint];
	} else if (controlPoint!=0) {
		[super moveComponentTo:point inView:view];
	}
}

- (void) moveConnectedComponentsBy:(NSPoint)change
{
	NSSet *selection = [[self modelObject] selection];

	EXPStockComponent *start = [self start];
	if ((start!=nil) && ![selection containsObject:start]) {
		NSPoint pos = [start pos];
		[start setPosition:NSMakePoint(pos.x + change.x, pos.y + change.y)];
		NSPoint pos2 = [start pos2];
		[start setPosition2:NSMakePoint(pos2.x + change.x, pos2.y + change.y)];
	}

	EXPStockComponent *end = [self end];
	if ((end!=nil) && ![selection containsObject:end]) {
		NSPoint pos = [end pos];
		[end setPosition:NSMakePoint(pos.x + change.x, pos.y + change.y)];
		NSPoint pos2 = [end pos2];
		[end setPosition2:NSMakePoint(pos2.x + change.x, pos2.y + change.y)];
	}
}

- (void) dump
{
	[super dump];

	EXPComponent *start = [self start];
	if (start!=nil) {
		printf("Start: %s\n", [[start name] UTF8String]);
	}
	
	EXPComponent *end = [self end];
	if (end!=nil) {
		printf("End: %s\n", [[end name] UTF8String]);
	}
	
}

- (void) addObjectsForDeletionTo:(id)array
{
	EXPStockComponent *start = [self start];
	if ([[start componentType] isEqualToString:@"Cloud"]) {
		if (![array containsObject:start]) {
			[array addObject:start];
			[self setStart:nil];
		}
	}
	
	EXPStockComponent *end = [self end];
	if ([[end componentType] isEqualToString:@"Cloud"]) {
		if (![array containsObject:end]) {
			[array addObject:end];
			[self setEnd:nil];
		}
	}
	
	[super addObjectsForDeletionTo:array];
}

- (BOOL) containsPoint:(NSPoint)point
{
	printf("%s %s containsPoint:(%g,%g)\n", [[self name] UTF8String], [[[self class] description] UTF8String], point.x, point.y);
	if ([self name]==nil) {
		printf("Here!\n");
		return NO;
	}
	if ([self path]==nil) {
		[self computePath];
	}
	NSArray *pathArray = [self path];
	BOOL containedInPath0 = [[pathArray objectAtIndex:0] containsPoint:point];
	BOOL containedInPath1 = [[pathArray objectAtIndex:1] containsPoint:point];
	BOOL containedInPath2 = [[pathArray objectAtIndex:2] containsPoint:point];
	BOOL contains = containedInPath0 || containedInPath1 || containedInPath2;
	
	return contains;
}

- (NSPoint) closestSurfacePointToPoint:(NSPoint)point
{
	EXPModelObject *modelObject = [self modelObject];
	NSDictionary *defaults = [modelObject defaults];
//	NSSize size = [self size];
	NSPoint pos = [self pos];
	float x = pos.x;
	float y = pos.y;
	NSPoint pos2 = [self pos2];
	float x2 = pos2.x;
	float y2 = pos2.y;
	float valveSize = [[defaults objectForKey:@"ValveRadius"] floatValue];
//	float radius = valveSize/2.0;
	float xc = (x + x2)/2.0;
	float yc = (y + y2)/2.0;
	
	float xp = point.x;
	float yp = point.y;
	float distance = hypot(xp - xc, yp - yc);
	float xs = xc + (xp - xc)*valveSize/distance;
	float ys = yc + (yp - yc)*valveSize/distance;

	return NSMakePoint(xs, ys);
}

- (NSRect) drawingBounds
{
	NSPoint pos = [self pos];
	NSSize size = [self size];
	NSRect rect = NSMakeRect(pos.x, pos.y, size.width, size.height);
	float x = pos.x;
	float y = pos.y;
	
	EXPModelObject *modelObject = [self modelObject];
	NSDictionary *attribs = [modelObject textAttributes];
	NSString *name = [self name];
	NSSize nameSize = [name sizeWithAttributes:attribs];
	float xp = pos.x + (size.width - nameSize.width)/2.0;
	float yp = pos.y - nameSize.height - 2.0;
	NSRect nameBounds = NSMakeRect(xp, yp, nameSize.width, nameSize.height);
	rect = NSUnionRect(rect, nameBounds);
	
	NSRect cloudBounds;
	float cloudHeight = 60.0;
	float couldWidth = 100.0;
	NSPoint pos2 = [self pos2];
	float x2 = pos2.x;
	float y2 = pos2.y;

	EXPComponent *start = [self start];
	if (start==nil) {
		float xc = x - 90.0;
		float yc = y - 25.0;
//		NSPoint position = NSMakePoint(xc, yc, );
		cloudBounds = NSMakeRect(xc, yc, couldWidth, cloudHeight);
		rect = NSUnionRect(rect, cloudBounds);
	}

	EXPComponent *end = [self end];
	if (end==nil) {
//		NSPoint position = NSMakePoint(x2, y2 - 25.0);
		float xc = x2 - 10.0;
		float yc = y2 - 25.0;
		cloudBounds = NSMakeRect(xc, yc, couldWidth, cloudHeight);
		rect = NSUnionRect(rect, cloudBounds);
	}

	return rect;
}

- (NSPoint) valvePosition
{
//	EXPModelObject *modelObject = [self modelObject];
//	NSDictionary *defaults = [modelObject defaults];

	NSPoint pos = [self pos];
	float x = pos.x;
	float y = pos.y;
//	NSRect rect = NSMakeRect(pos.x, pos.y, size.width, size.height);
	NSPoint pos2 = [self pos2];
	float x2 = pos2.x;
	float y2 = pos2.y;
//	float arrowheadSize = [[defaults objectForKey:@"FlowArrowheadSize"] floatValue];
//	float valveSize = [[defaults objectForKey:@"ValveRadius"] floatValue];
	float xc = (x + x2)/2.0;
	float yc = (y + y2)/2.0;

	return NSMakePoint(xc, yc);
}

- (void) drawName
{
	EXPModelObject *modelObject = [self modelObject];

/*	NSPoint pos = [self pos];
	NSSize size = [self size];
	float xp = pos.x + size.width/2.0;
	float yp = pos.y; */

	NSSize size = [self size];
	NSString *namePosition = [self namePosition];
	NSPoint valvePos = [self valvePosition];
	
	NSString *name = [self name];
	NSDictionary *attribs = [modelObject textAttributes];
	NSSize nameSize = [name sizeWithAttributes:attribs];

	NSPoint namePos;
	if ([namePosition isEqualToString:@"Above"]) {
		namePos = NSMakePoint(valvePos.x - nameSize.width/2.0, valvePos.y - nameSize.height - 2.0);
	} else {
		namePos = NSMakePoint(valvePos.x - nameSize.width/2.0, valvePos.y + size.height);
	}

	[name drawAtPoint:namePos withAttributes:attribs];
}

- (void) computePath
{
	EXPModelObject *modelObject = [self modelObject];
	NSDictionary *defaults = [modelObject defaults];
	
	NSPoint pos = [self pos];
//	float x = pos.x;
//	float y = pos.y;
	NSPoint pos2 = [self pos2];
	float x2 = pos2.x;
	float y2 = pos2.y;
	float arrowheadSize = [[defaults objectForKey:@"FlowArrowheadSize"] floatValue];
	float valveSize = [[defaults objectForKey:@"ValveRadius"] floatValue];
	NSPoint valvePosition = [self valvePosition];
	float xc = valvePosition.x;
	float yc = valvePosition.y;
	
	NSBezierPath *path1 = [[NSBezierPath alloc] init];
	
	NSPoint controlVector2 = [self controlVector2];
	float cv2length = hypotf(controlVector2.x, controlVector2.y);
	[path1 setLineWidth:[[defaults objectForKey:@"FlowThickness"] floatValue]];
	[path1 moveToPoint:pos];
	[path1 lineToPoint:NSMakePoint(x2 - arrowheadSize*controlVector2.x/cv2length, y2 - arrowheadSize*controlVector2.y/cv2length)];

	NSBezierPath *arrowPath = [self arrowHeadAt:pos2 inDirection:controlVector2 size:NSMakeSize(arrowheadSize, arrowheadSize/2.0)];
	
//	NSPoint controlVector2 = [self controlVector2];
//	[self arrowheadAt:pos2 inDirection:controlVector2 size:NSMakeSize(arrowheadSize, arrowheadSize/2.0)];

	NSBezierPath *path2 = [[NSBezierPath alloc] init];
	NSRect valveRect = NSMakeRect(xc - valveSize, yc - valveSize, 2.0*valveSize, 2.0*valveSize);
	[path2 appendBezierPathWithOvalInRect:valveRect];
	[path2 setLineWidth:2.0];
//	[path2 stroke];
	
//	[NSBezierPath strokeLineFromPoint:NSMakePoint(xc - valveSize*0.707, yc + valveSize*0.707) toPoint:NSMakePoint(xc + valveSize*0.707, yc - valveSize*0.707)];
//	[NSBezierPath strokeLineFromPoint:NSMakePoint(xc - valveSize*0.707, yc - valveSize*0.707) toPoint:NSMakePoint(xc + valveSize*0.707, yc + valveSize*0.707)];
	NSBezierPath  *path3 = [[NSBezierPath alloc] init];
	[path3 moveToPoint:NSMakePoint(xc - valveSize*0.707, yc + valveSize*0.707)];
	[path3 lineToPoint:NSMakePoint(xc + valveSize*0.707, yc - valveSize*0.707)];
	
	NSBezierPath  *path4 = [[NSBezierPath alloc] init];
	[path4 moveToPoint:NSMakePoint(xc - valveSize*0.707, yc - valveSize*0.707)];
	[path4 lineToPoint:NSMakePoint(xc + valveSize*0.707, yc + valveSize*0.707)];
	
	NSArray *pathArray = [[NSArray alloc] initWithObjects:path1, arrowPath, path2, path3, path4, nil];
	[path1 release];
//	[arrowPath release];
	[path2 release];
	[path3 release];
	[path4 release];

	[self setPath:pathArray];
	[pathArray release];
	
}

- (void) drawInView:(id)view isSelected:(BOOL)flag
{
//	NSLog(@"Drawing flow: %@", [self name]);
	EXPModelObject *modelObject = [self modelObject];
	NSDictionary *defaults = [modelObject defaults];
	
	if ([self path]==nil) {
		[self computePath];
	}
	NSArray *pathArray = [self path];
	[[defaults objectForKey:@"FlowStrokeColour"] set];
	[[pathArray objectAtIndex:0] stroke];
	[[pathArray objectAtIndex:1] fill];

	[[NSColor blackColor] set];
	[[pathArray objectAtIndex:2] stroke];
	[[defaults objectForKey:@"ValveFillColour"] set];
	[[pathArray objectAtIndex:2] fill];
	[[NSColor blackColor] set];
	[[pathArray objectAtIndex:3] stroke];
	[[pathArray objectAtIndex:4] stroke];
	
	if (flag) {
		NSRect bounds = [[pathArray objectAtIndex:2] bounds];
		NSPoint pos = bounds.origin;
		NSSize size = bounds.size;
		float controlSquareSize = [[defaults objectForKey:@"ControlSquareSize"] floatValue];
		[[NSColor magentaColor] set];

		NSFrameRect(NSMakeRect(pos.x - controlSquareSize/2.0, pos.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize));
		NSFrameRect(NSMakeRect(pos.x + size.width - controlSquareSize/2.0, pos.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize));
		NSFrameRect(NSMakeRect(pos.x - controlSquareSize/2.0, pos.y + size.height - controlSquareSize/2.0, controlSquareSize, controlSquareSize));
		NSFrameRect(NSMakeRect(pos.x + size.width - controlSquareSize/2.0, pos.y + size.height - controlSquareSize/2.0, controlSquareSize, controlSquareSize));

		pos = [self pos];
		NSPoint pos2 = [self pos2];
		NSBezierPath *path;
		NSPoint controlVector = [self controlVector];
		NSPoint controlVector2 = [self controlVector2];
		NSPoint controlPoint1 = NSMakePoint(pos.x + controlVector.x,  pos.y + controlVector.y);
		NSPoint controlPoint2 = NSMakePoint(pos2.x - controlVector2.x,  pos2.y - controlVector2.y);
		path = [NSBezierPath bezierPathWithOvalInRect:
			NSMakeRect(controlPoint1.x - controlSquareSize/2.0, controlPoint1.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize)];
		[path stroke];
		path = [NSBezierPath bezierPathWithOvalInRect:
			NSMakeRect(controlPoint2.x - controlSquareSize/2.0, controlPoint2.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize)];
		[path stroke];

		NSFrameRect(NSMakeRect(pos.x - controlSquareSize/2.0, pos.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize));
		NSFrameRect(NSMakeRect(pos2.x - controlSquareSize/2.0, pos2.y - controlSquareSize/2.0, controlSquareSize, controlSquareSize));
		
		[NSBezierPath strokeLineFromPoint:pos toPoint:controlPoint1];
		[NSBezierPath strokeLineFromPoint:pos2 toPoint:controlPoint2];
	}

	[self drawName];
}

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

@end