#import "IcaoMap.h"
#import "mapobjects.h"
#import "MapController.h"
#import <appkit/appkit.h>

@implementation IcaoMap


- awakeFromNib
{
	NXPoint             hotSpot;

	cursor = [[NXCursor alloc]
						initFromImage:[NXImage findImageNamed:"CrossHair"]];

	hotSpot.x = 8.0;
	hotSpot.y = 8.0;

	[cursor setHotSpot:&hotSpot];

	trackingRect = 0;
	[self setMyTrackingRect:YES];

	return self;
}

- (BOOL)acceptsFirstResponder
{
	return YES;
}


- infoPopUp
{
	return infoPopUp;
}

- setMyTrackingRect:(BOOL)flag
{
	NXRect              visible;

	if (trackingRect)
	{
		[window discardTrackingRect:trackingRect];
		trackingRect = 0;
	}
	if (flag)
	{
		if ([self getVisibleRect:&visible] && [window isVisible])
		{
			if ([[window delegate] mapZone] != theZone)
				[[window delegate] copyDataToGlobal];
			else
				[[window delegate] copyDataFromGlobal];		
			[self convertRect:&visible toView:NULL];
			[window setTrackingRect:&visible inside:NO owner:self tag:1
			 left:NO right:NO];
			trackingRect = 1;
		}
	}
	return self;
}


- resetCursorRects
{
	NXRect              visible;

	if ([self getVisibleRect:&visible])
		[self addCursorRect:&visible cursor:cursor];
	return self;
}


- mouseEntered:(NXEvent *)theEvent
{
	inside = YES;
	[window makeFirstResponder:self];
	[window addToEventMask:NX_MOUSEMOVEDMASK];

	return self;
}

- mouseMoved:(NXEvent *)theEvent
{
	if (inside)
	{
		NXPoint             p;
		BOOL                shift, control;
		LOCATION            pointer;

		shift = (theEvent->flags & NX_SHIFTMASK) ? YES : NO;
		control = (theEvent->flags & NX_CONTROLMASK) ? YES : NO;

		p = theEvent->location;
		[self convertPoint:&p fromView:nil];
		pointer = window2internal(p.x, p.y);
		[locationField setStringValue:internal2string(pointer)];
#ifndef timetest
		[timeField setStringValue:nearobject(p.x, p.y)];
#endif
	}
	return self;
}

- mouseExited:(NXEvent *)theEvent
{
	[window removeFromEventMask:NX_MOUSEMOVEDMASK];
	[locationField setStringValue:""];
	[timeField setStringValue:""];
	inside = NO;
	return self;
}

- runRubberband
{
#define LINEWIDTH 2
	/* At this point rubberStart is already set */
	NXEvent            *theEvent;
	BOOL                shouldLoop = YES;
	int                 oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
	char                text[80];
	char               *name;
	int                 xtmp, ytmp;
	int                 x1, y1, x2, y2;
	NXRect              old, new;
	NXPoint             p2;

	[self lockFocus];
	internal2window(rubberStart, &x1, &y1);
	gp_setlinestyle(LINEWIDTH, SOLID);
	gp_setcolor(RED);
	NXSetRect(&old, x1 - LINEWIDTH, x2 - LINEWIDTH, LINEWIDTH * 2, LINEWIDTH * 2);
	new = old;
	PSsetinstance(YES);
	while (shouldLoop)
	{
		NXPing();
		theEvent = [NXApp getNextEvent:(NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK)];
		if (theEvent->type == NX_LMOUSEUP)
		{
			PShideinstance(NX_X(&old), NX_Y(&old), NX_WIDTH(&old), NX_HEIGHT(&old));
			shouldLoop = NO;
		}
		else
		{
			p2 = theEvent->location;
			[self convertPoint:&p2 fromView:nil];

			rubberEnd = window2internal(p2.x, p2.y);
			if (!(theEvent->flags & NX_SHIFTMASK))
				//snap ?
			{
				/* This also sets the variable saveobj. aarg */
				name = nearobject(p2.x, p2.y);

				if (saveobj)
					/* This somehow checks for Areas */
					if (saveobj->type / 10 != O_CTR / 10)
					{
						rubberEnd = saveobj->location;
						internal2window(rubberEnd, &xtmp, &ytmp);
						p2.x = xtmp;
						p2.y = ytmp;
						[locationField setStringValue:name];
					}
					else
						[locationField setStringValue:internal2string(rubberEnd)];
				else
					[locationField setStringValue:internal2string(rubberEnd)];
			}
			else
				[locationField setStringValue:internal2string(rubberEnd)];
			sprintf(text, "dist: %3.3f %s  track: %3.2f"
							, distance(rubberStart, rubberEnd) * UNIT_CHANGE(IPD_RUBBERUNIT)
							, UNIT_NAME(IPD_RUBBERUNIT)
							,	truetrack(rubberStart, rubberEnd));


#ifndef timetest
			[timeField setStringValue:text];
#endif

			internal2window(rubberEnd, &x2, &y2);
			NXSetRect(&new, MIN(x1, x2) - LINEWIDTH, MIN(y1, y2) - LINEWIDTH
								,ABS(x1 - x2) + LINEWIDTH * 2, ABS(y1 - y2) + LINEWIDTH * 2);
			if (!NXEqualRect(&old, &new))
			{
				PShideinstance(NX_X(&old), NX_Y(&old), NX_WIDTH(&old)
											 ,NX_HEIGHT(&old));
				old = new;
				gp_drawline(x1, y1, x2, y2);
			}
		}
	}
	[window setEventMask:oldMask];

	/* to hide the rubberband */
	[self mouseMoved:theEvent];
	PSsetinstance(NO);
	[self unlockFocus];
	return self;
#undef LINEWIDTH
}

- mouseDown:(NXEvent *)theEvent
{
	switch (theEvent->data.mouse.click)
	{
	case 2:
		{
			NXPoint             p;

			/* Get the new center of the map */
			p = theEvent->location;
			[self convertPoint:&p fromView:NULL];
			map_origin = window2internal(p.x, p.y);
			map_initcoord();

			/* Update the Coordinate Info */
			[self mouseMoved:theEvent];

			[self display];
			break;
		}
	case 1:
		{
			NXPoint             p;
			char               *name;

			p = theEvent->location;
			[self convertPoint:&p fromView:nil];
			rubberStart = window2internal(p.x, p.y);
			if (!(theEvent->flags & NX_SHIFTMASK))
			{
				/* This also sets the global variable saveobj! shudder! */
				name = nearobject(p.x, p.y);
				if (saveobj)
				{
					/* This somehow checks for Areas */
					if (saveobj->type / 10 != O_CTR / 10)
					{
						rubberStart = saveobj->location;
					}
				}
			}
			[self runRubberband];
			break;
		}
	}
	return self;
}

- removeInfoPopUp
{
	if (infoPopUp)
	{
		infoPopUp = [infoPopUp close];
	}
	return self;
}

- rightMouseUp:(NXEvent *)theEvent
{
	return[self removeInfoPopUp];
}

- rightMouseDown:(NXEvent *)theEvent
{
	char               *(savelines[MV_MAX_LINES]);
	int                 savenumlines;
	int                 i;
	TextField          *aTextField;
	NXPoint             p = theEvent->location;
	NXRect              aRect;
	char               *string = malloc(MV_MAX_LINES * MV_MAX_LEN);

	[self convertPoint:&p fromView:NULL];

	for (i = 0; i < MV_MAX_LINES; i++)
		savelines[i] = malloc(MV_MAX_LEN);

	objectdescription(savelines, &savenumlines, p.x, p.y, &i);
	if (savenumlines != 0)
	{
		strcpy(string, savelines[0]);
		for (i = 1; i < savenumlines; i++)
		{
			/* OM I think this is not a nice way to do it */
			sprintf(string, "%s\n%s", string, savelines[i]);
		}

		/* initialize with *any* frame */
		aTextField = [[TextField alloc] initFrame:&frame];

		[aTextField setSelectable:YES];
		[aTextField setBordered:!(theEvent->flags & NX_CONTROLMASK)];
		[aTextField setBackgroundGray:NX_LTGRAY];
		[aTextField setBackgroundColor:NX_COLORLTGRAY];
		[aTextField setStringValue:string];
		[aTextField sizeToFit];
		[aTextField getFrame:&aRect];

		infoPopUp = [[Window alloc] initContent:&aRect
		 style:theEvent->flags & NX_CONTROLMASK ? NX_TITLEDSTYLE : NX_PLAINSTYLE
								 backing:NX_RETAINED
								 buttonMask:NX_CLOSEBUTTONMASK | NX_MINIATURIZEBUTTONMASK
								 defer:YES];

		[[infoPopUp setContentView:aTextField] free];
		p = theEvent->location;
		[window convertBaseToScreen:&p];
		p.x -= aRect.size.width / 2;
		p.y -= aRect.size.height + 10;

		[infoPopUp moveTo:p.x:p.y];
		[infoPopUp setTitle:"Info"];
		[infoPopUp orderFront:self];
		if (theEvent->flags & NX_CONTROLMASK)
			infoPopUp = NULL;
	}
	for (i=0; i < MV_MAX_LINES; i++)
		free(savelines[i]);
	free(string);

	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	if ([[window delegate] mapZone] != theZone)
		[[window delegate] copyDataToGlobal];
	else
		[[window delegate] copyDataFromGlobal];		
#ifdef timetest
	[super drawSelf:rects :rectCount];
	[timeField setIntValue:Elapsed];
	return self;
#else
	return [super drawSelf:rects :rectCount];
#endif
}

@end
