

/* Generated by Interface Builder */

#import "InputView.h"
#import "ControlObject.h"
#import <appkit/appkit.h>
#import <math.h>
#import "constants.h"
#import "pointdata.h"


void doIMapping(PointList *p)
{
	int t;
	Point *pt;
	int lastpen = UP;
	float lastcol = NX_LTGRAY;

	PSsetgray(NX_LTGRAY);
	PSsetlinewidth(0.0);
	for(t=gotoPointInList(p,0,&pt);(t);t=gotoNextPointInList(p,&pt)) {
		if (lastpen == DOWN)
			PSlineto(pt->x,pt->y);
		else
			PSmoveto(pt->x,pt->y);
		if (lastcol != pt->pencolour) {
			PSstroke();
			PSnewpath();
			PSmoveto(pt->x,pt->y);
			PSsetgray(pt->pencolour);
		}
		lastpen = pt->pen;
		lastcol = pt->pencolour;
	}
	PSstroke();
}

float AngleBetween(Point *pa, Point *pb, Point *p)
{
	Point v1,v2;
	float f;
	v1.x = pa->x - p->x;
	v1.y = pa->y - p->y;
	v2.x = pb->x - p->x;
	v2.y = pb->y - p->y;
	f = ((v1.x * v2.x) + (v1.y * v2.y));
	if (f == 0.0)
		return (PI/2);
	return fabs(acos(f/sqrt(fabs(f))));
}

@implementation InputView

- (BOOL)gridState
{
	return GridOn;
}

- setGridState:(BOOL)newGridState
{
	GridOn = newGridState;
	return self;
}

- setItUp:sender
{
	caller = sender;
	inputMode = [sender inputMode];
	inputX = [sender inputX];
	inputY = [sender inputY];
	inputPen = [sender inputPen];
	[self setDrawSize:(IRight-ILeft) :(ITop-IBottom)];
	[self setDrawOrigin:ILeft :IBottom];
	[[self window] addToEventMask:
							NX_LMOUSEDRAGGEDMASK|NX_MOUSEMOVEDMASK|NX_FLAGSCHANGEDMASK];
	[[self window] makeFirstResponder:self];
	instanceDrawing = NO;
	state = WAITING;
	newPointList(&currentPoints);
	return self;
}

- drawGrid
{
	float f;
	for(f=ILeft;(f<=IRight);f=f+(PI/12)) {
		PSmoveto(f,ITop);
		PSlineto(f,IBottom);
	}
	for(f=0.0;(f<=ITop);f=f+(PI/12)) {
		PSmoveto(ILeft,f);
		PSlineto(IRight,f);
	}
	for(f=0.0;(f>=IBottom);f=f-(PI/12)) {
		PSmoveto(ILeft,f);
		PSlineto(IRight,f);
	}
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	if (instanceDrawing) {
		PSsetinstance(YES);
		[self doInstanceDragSwitch];
	} else {
		PSsetinstance(NO);
		NXEraseRect(&bounds);
		PSsetgray(NX_LTGRAY);
		PSsetlinewidth(0.0);
		if (GridOn == YES)
			[self drawGrid];
		doIMapping(&currentPoints);
	}
	return self;
}

-saveFile
{
	if (!savePanel) {
		savePanel = [SavePanel new];
		[savePanel setRequiredFileType:"map"];
	}
	if ([savePanel runModal])
		if (savePointList(&currentPoints,(char *)[savePanel filename]))
			return self;
		else
			NXRunAlertPanel("File System Error.","MapMaker couldn't save your map.",NULL,NULL,NULL);
	else
		return self;
}

- openFile:(const char *)filename
{
	freePointList(&currentPoints);
	newPointList(&currentPoints);
	if (!(loadPointList(&currentPoints,(char *)filename)))
		NXRunAlertPanel("File System Error.","MapMaker couln't load %s.",NULL,NULL,NULL,filename);
	[caller getNewInput];
	[caller refreshOutput];
	[self display];
	return self;
}

-openFile
{	
	if (!openPanel) {
		openPanel = [OpenPanel new];
		[openPanel setRequiredFileType:"map"];
	}
	if ([openPanel runModal]) {
		[self openFile:[openPanel filename]];
	}
	return self;
}

-clearFile
{
	freePointList(&currentPoints);
	newPointList(&currentPoints);
	[self display];
	[caller getNewInput];
	[caller refreshOutput];
	return self;
}

- map:(PointList **)pl
{
	*pl = &currentPoints;
	return self;
}

- mouseDown:(NXEvent *)theEvent
{
	location = &(theEvent->location);
	switch (state) {
		case DELETING : 	instanceDrawing = YES;
						[self display];
						break;
		case INSERTING : [self insertPoint:theEvent];
						state = DMOVING;
						MoveIndex = InsertIndex;
						[self display];
						break;
		case MOVING :	instanceDrawing = YES;
						[self display];
						break;
		default :	instanceDrawing = YES;
				[self display];
				break;
	}
	return self;
}

- mouseUp:(NXEvent *)theEvent
{
	switch (state) {
		case DELETING :
					[self deletePoint:theEvent];
					instanceDrawing = NO;
					[self display];
					break;
		case INSERTING :
					instanceDrawing = NO;
					[self display];
					break;
		case MOVING :  break;
		case DMOVING :
					[self movePoint:theEvent];
					instanceDrawing = NO;
					[self display];
					[self fixState:theEvent];
					break;
		default :		[self addPoint:theEvent];
					instanceDrawing = NO;
					[self display];
					break;
	}
	return self;
}

- insertPoint:(NXEvent *)theEvent
{
	NXPoint pt;
	Point p,*p1,*p2,*p3;
	int s,t,u;
	float f,g;
	
	pt = theEvent->location;
	[self convertPoint:&pt fromView:nil];
	p.x = pt.x;
	p.y = pt.y;
	p.pen = DOWN;
	p.pencolour = NX_BLACK;
	if (theEvent->flags&NX_ALTERNATEMASK) 
		p.pen = UP;
	s = gotoPointInList(&currentPoints,InsertIndex-1,&p1);
	if (s)
		t = gotoNextPointInList(&currentPoints,&p2);
	else
		t = gotoPointInList(&currentPoints,InsertIndex,&p2);
	if (t)
		u = gotoNextPointInList(&currentPoints,&p3);
	else
		u = gotoPointInList(&currentPoints,InsertIndex+1,&p3);
	if ((s) && (t) && (u)) {
		f = AngleBetween(p1,&p,p2);
		g = AngleBetween(p3,&p,p2);
		if (f > g) {
			InsertIndex++;
		}
	} else if ((s) && (t)) {
		f = AngleBetween(p1,&p,p2);
		if (f > (PI/2)) {
			InsertIndex++;
		}
	} else if ((t) && (u)) { 
		f = AngleBetween(p3,&p,p2);
		if (f <= (PI/2)) {
			InsertIndex++;
		}
	}  else {
		InsertIndex = 0;
	}
	/* find slopes. */
		
	s= insertInPointListAt(&currentPoints,&p,InsertIndex);
	return self;
}

- movePoint:(NXEvent *)theEvent
{
	NXPoint pt;
	Point *p;
	int s;
	
	pt = theEvent->location;
	[self convertPoint:&pt fromView:nil];
	s = gotoPointInList(&currentPoints,MoveIndex,&p);
	p->x = pt.x;
	p->y = pt.y;
	return self;
}

- addPoint:(NXEvent *)theEvent
{
	NXPoint pt;
	Point p;

	pt = theEvent->location;
	[self convertPoint:&pt fromView:nil];

	p.x = pt.x;
	p.y = pt.y;
	p.pen = DOWN;
	p.pencolour = NX_BLACK;
	if (theEvent->flags&NX_ALTERNATEMASK) 
		p.pen = UP;
		
	addToPointList(&currentPoints,&p);
	return self;
}

- deletePoint:(NXEvent *)theEvent
{
	int s,index;
	NXPoint pt;
	Point *p;

	pt = theEvent->location;
	[self convertPoint:&pt fromView:nil];

	s = nearestPointInList(&currentPoints,&p,pt.x,pt.y,&index);
	if ((!s) ||  ((POINTRADIUS * POINTRADIUS) <  (((pt.x - p->x) * (pt.x - p->x)) + ((pt.y - p->y) * (pt.y - p->y)))))
		return self;
	removeFromPointList(&currentPoints,index);
	return self;
}

- doInstanceDragSwitch
{
	NXPoint pt;
	Point *p, *p1,*p2, *p3;
	int s,t,u;
	int index;
	int hold1,hold2;
	
	pt = *location;
	[self convertPoint:&pt fromView:nil];
	
	switch(state) {
		case DELETING :
				s = nearestPointInList(&currentPoints,&p,pt.x,pt.y,&index);
				if ((!s) ||  ((POINTRADIUS * POINTRADIUS) < 
							(((pt.x - p->x) * (pt.x - p->x)) + ((pt.y - p->y) * (pt.y - p->y)))))
					{
					PSnewinstance();
					break;
				}
				PSsetgray(NX_BLACK);
				PSsetlinewidth(0.0);
				PSnewinstance();
				PSarc(p->x,p->y,POINTRADIUS,(2*PI),PI);
				PSstroke();
				break;
		case INSERTING :
				s = nearestPointInList(&currentPoints,&p,pt.x,pt.y,&index);
				if ((!s) || ((POINTRADIUS * POINTRADIUS) < 
							(((pt.x - p->x) * (pt.x - p->x)) + ((pt.y - p->y) * (pt.y - p->y))))) {
					PSnewinstance();
					break;
				}
				InsertIndex = index;
				PSsetgray(NX_BLACK);
				PSsetlinewidth(0.0);
				PSnewinstance();
				PSarc(pt.x,pt.y, POINTRADIUS,(2*PI),PI);
				PSstroke();
				break;
		case MOVING :
				s = nearestPointInList(&currentPoints,&p,pt.x,pt.y,&index);
				if ((!s) || ((POINTRADIUS * POINTRADIUS) < 
							(((pt.x - p->x) * (pt.x - p->x)) + ((pt.y - p->y) * (pt.y - p->y)))))
					{
					PSnewinstance();
					break;
				}
				MoveIndex = index;
				PSsetgray(NX_BLACK);
				PSsetlinewidth(0.0);
				PSnewinstance();
				PSarc(p->x,p->y,POINTRADIUS,(2*PI),PI);
				PSstroke();
				break;
		case DMOVING :
				s =gotoPointInList(&currentPoints,MoveIndex-1,&p2);
				if (s)
					u = gotoNextPointInList(&currentPoints,&p1);
				else
					u = gotoPointInList(&currentPoints,MoveIndex,&p1);
				if (u)
					t = gotoNextPointInList(&currentPoints,&p3);
				else
					t = gotoPointInList(&currentPoints,MoveIndex+1,&p3);
				PSsetgray(NX_BLACK);
				PSsetlinewidth(0.0);
				PSnewinstance();
				if ((s) && (p2->pen == DOWN)) {
					PSsetgray(NX_BLACK);
					PSmoveto(p2->x,p2->y);
					PSlineto(pt.x,pt.y);
				} else {
					PSmoveto(pt.x,pt.y);
				}
				if ((t) && (u) && (p1->pen == DOWN)) {
					PSlineto(p3->x,p3->y);
				}
				PSstroke();
				break;
		default :	s = lastPointInList(&currentPoints,&p);
				if ((!s) || (p->pen == UP)) return self;
				PSsetgray(NX_BLACK);
				PSsetlinewidth(0.0);
				PSnewinstance();
				PSmoveto(p->x,p->y);
				PSlineto(pt.x,pt.y);
				PSstroke();
				break;
	}
	return self;
}

- mouseDragged:(NXEvent *)theEvent
{
	NXPoint pt;
	char str[20];
	
	pt = *location;
	[self convertPoint:&pt fromView:nil];
	
	sprintf(str,"%.1f",(pt.x)*(180/PI));
	[inputX setStringValue:str];
	sprintf(str,"%.1f",(pt.y)*(180/PI));
	[inputY setStringValue:str];

	switch (state) {
		case DELETING :	
						location = &(theEvent->location);
						[self display];
						break;
		case INSERTING : ;
		case MOVING : state = DMOVING;
		case DMOVING : location = &(theEvent->location);
						[self display];
						break;
		default:	location = &(theEvent->location);
				 [self display];
				 break;
	}
	return self;
}

- mouseMoved:(NXEvent *)theEvent
{
	NXPoint pt;
	char str[20];
	
	pt = theEvent->location;
	[self convertPoint:&pt fromView:nil];
	
	sprintf(str,"%.1f",(pt.x)*(180/PI));
	[inputX setStringValue:str];
	sprintf(str,"%.1f",(pt.y)*(180/PI));
	[inputY setStringValue:str];
	
	switch (state) {

		case MOVING :
					instanceDrawing = YES;
					location = &(theEvent->location);
					[self display];
					break;
		case DELETING :
					instanceDrawing = YES;
					location = &(theEvent->location);
					[self display];
					break;
		case INSERTING :
					instanceDrawing = YES;
					location = &(theEvent->location);
					[self display];
					break;
		default: 	if(instanceDrawing) {
					instanceDrawing = NO;
					[self display];
				}
				break;
	}
}

- fixState:(NXEvent *)theEvent
{
	state = WAITING;
	if (theEvent->flags&NX_ALPHASHIFTMASK)
		state = DELETING;
	if (theEvent->flags&NX_CONTROLMASK)
		state = INSERTING;
	if (theEvent->flags&NX_COMMANDMASK)
		state = MOVING;
	
	switch (state) {
		case DELETING : [inputMode setStringValue:"Deleting"]; break;
		case INSERTING : [inputMode setStringValue:"Inserting"]; break;
		case MOVING : [inputMode setStringValue:"Moving"]; break;
		case WAITING : [inputMode setStringValue:"Waiting"]; break;
		default : [inputMode setStringValue:"Barfing"]; break;
	}
	return self;
}

- flagsChanged:(NXEvent *)theEvent
{
	if (theEvent->flags&NX_ALTERNATEMASK)
		[inputPen setStringValue:"Up"];
	else
		[inputPen setStringValue:"Down"];
	[self fixState:theEvent];
	return self;
}
@end
