#import "FlyView.h"
#import "Thinker.h"

#import <stdio.h>
#import <string.h>
#import <sys/types.h>

@implementation FlyView

- oneStep
{
	static BStimeval	then = 0;
	static unsigned int	i;
	static BOOL			didSwitchState;

	didSwitchState = NO;

	for (i = 0; i < flyCount; i++) {
		currentFly = [flyList objectAt:i];
		if ([currentFly isSplat]) {
			[[currentFly eraseGuts] toggleSplat:NO];
			[flyLogic randomPosition:currentFly];
		} else {
			[currentFly eraseFly];
			if ((currentTimeInMs() - then) > [currentFly statePeriod]) {
				didSwitchState = YES;
				[currentFly setState:![currentFly isFirstState]];
			}
			[[flyLogic getNewHeading:currentFly] adjustPosition:currentFly];
			[[currentFly setCellRect] drawFly];
		}	
	}

	if (didSwitchState) {
		then = currentTimeInMs();
	}

	mach_sleep(port, frameRate);

	return self;
}

- initFrame:(NXRect *)frameRect
{
	struct timeval	tp;
	char			buf[MAXPATHLEN + 1];
	long			i;

	[super initFrame:frameRect];
	
	gettimeofday(&tp, (struct timezone *)0L);
	srandom(tp.tv_sec);

	moduleDir = [[NXApp delegate] moduleDirectory:"Fly"];
	strcpy(buf, moduleDir);
	strcat(buf, "/Fly.bundle");
	flyBundle = [[NXBundle alloc] initForDirectory:buf];

	[flyBundle getPath:buf forResource:"fly" ofType:"tiff"];
	flyImage = [[NXImage alloc] initFromFile:buf];

	[flyBundle getPath:buf forResource:"guts" ofType:"tiff"];
	gutsImage = [[NXImage alloc] initFromFile:buf];

	[flyBundle getPath:buf forResource:"flyMask" ofType:"tiff"];
	maskImage = [[NXImage alloc] initFromFile:buf];

	[flyBundle getPath:buf forResource:"splat" ofType:"snd"];
	splatSnd = [[Sound alloc] initFromSoundfile:buf];

	[self inspector:self];
	[[infoText	setEditable:NO] setSelectable:YES];  // for 3.0pr IB bug

	NXRegisterDefaults([NXApp appName], defs);
	[self getDefaults:self];
	[self setAnimationSpeed:animationSlider];
	[self setNumFlies:flyCountSlider];

	flyWindow = [self window];
	[flyWindow addToEventMask:NX_LMOUSEDOWNMASK];

	maxFlyCount = (unsigned int)[(Slider *)flyCountSlider maxValue];

	flyLogic = [[FlyLogic alloc] init];
	[flyLogic setBoundsRect:&bounds];

	flyList = [[List alloc] initCount:maxFlyCount];

	for (i = 0; i < flyCount; i++) {
		currentFly = [self createFly];
		[flyList addObject:currentFly];
	}

	port = mach_sleep_install();

	return self;
}

- (Fly *)createFly
{
	Fly		*fly;

	fly = [[Fly alloc] init];	
	[fly setFlyImage:flyImage];
	[fly setGutsImage:gutsImage];
	[fly setMaskImage:maskImage];
	[fly setSplatSnd:splatSnd];
	[flyLogic randomPosition:fly];
	[fly setCellRect];
	[self setFlySpeed:speedSlider forFly:fly];
	[flyLogic getStateAndTurn:fly];

	return(fly);
}

- free
{
	mach_sleep_remove(port);

	[[flyList freeObjects]	free];
	
	[flyImage free];
	[gutsImage free];
	[splatSnd free];

	[flyLogic free];

	[flyBundle free];
	
	return [super free];
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	if (!rects || !rectCount) return self;
	
	if (kBackgroundGray != NX_BLACK) {
		PSsetgray(kBackgroundGray);
		NXRectFill(rects);
	}

	return self;
}

- mouseDown:(NXEvent *)theEvent;
{
	NXRect			r;
	NXPoint			p;
	NXPoint			*flyPos;
	unsigned int	i;

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

	for (i = 0; i < flyCount; i++) {
		currentFly = [flyList objectAt:i];
		if (![currentFly isSplat]) {
			flyPos = [currentFly position];			
			NXSetRect(&r, flyPos->x, flyPos->y, kFlyCellWidth, kFlyCellHeight);			
			if (NXPointInRect(&p, &r)) {
				[self lockFocus];
					[currentFly doSplat];
				[self unlockFocus];
			}
		}
	}
	return self;
}

- newWindow
{
	flyWindow = [self window];
	[flyWindow addToEventMask:NX_LMOUSEDOWNMASK];
	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[flyLogic setBoundsRect:&bounds];

	return self;
}

- (const char *) windowTitle
{
	return("Catch the Buzz...");
}

- inspector:sender
{
	char	buf[MAXPATHLEN + 1];

	if (!inspectorPanel) {
		[flyBundle getPath:buf forResource:"FlyView" ofType:"nib"];
		[NXApp loadNibFile:buf owner:self withNames:NO];
	}
	return inspectorPanel;
}

- inspectorWillBeRemoved
{
	[[infoText window] orderOut:self];
	return self;
}

- setSpeed:sender
{
	unsigned int 	i;

	for (i = 0; i < [flyList count]; i++) {
		[self setFlySpeed:sender forFly:[flyList objectAt:i]];
	}
	return self;
}

- setFlySpeed:(Slider *)sender forFly:(Fly *)fly
{
	float	speed;

	speed = [sender floatValue];
	if (speed < 1) speed = 1;

	[fly setSpeedValue:speed];
	[flyLogic getStateAndTurn:fly];
	[self delayedDefaults:self];

	return self;
}

- setAnimationSpeed:(Slider *)sender
{
	frameRate = ([sender maxValue] + [sender minValue]) - [sender intValue];
	[self delayedDefaults:self];
	return self;
}

- setNumFlies:sender
{
	unsigned int	tmpFlyCount;
	int				flyDiff;

	tmpFlyCount = flyCount;
	flyCount = [flyCountSlider intValue];
	[flyCountBox setIntValue:flyCount];
	
	flyDiff = abs(flyCount - tmpFlyCount);

	if (flyCount > tmpFlyCount) {
		while(flyDiff--) {
			[flyList addObject:[self createFly]];
			if ([self canDraw]) {
				[self lockFocus];
					[[flyList lastObject] drawFly];
				[self unlockFocus];
			}
		}
	} else if (flyCount < tmpFlyCount) {
		if ([self canDraw]) {
			[self lockFocus];
				while(flyDiff--) {
					[[[flyList removeLastObject] eraseFly] free];
				}
			[self unlockFocus];
		}
	}

	[self delayedDefaults:self];
	return self;
}

- delayedDefaults:sender
{
	[self perform:@selector(writeDefaults:) with:self afterDelay:kSliderDelay cancelPrevious:YES];
	return self;
}

- getDefaults:sender
{
	[speedSlider setFloatValue:atof(NXGetDefaultValue([NXApp appName], "FlySpeed"))];
	[animationSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName], "AnimationSpeed"))];
	[flyCountSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName], "NumFlies"))];
	[flyCountBox setIntValue:[flyCountSlider intValue]];

	return self;
}

- writeDefaults:sender
{
	NXWriteDefault([NXApp appName], "FlySpeed", [speedSlider stringValue]);
	NXWriteDefault([NXApp appName], "AnimationSpeed", [animationSlider stringValue]);
	NXWriteDefault([NXApp appName], "NumFlies", [flyCountSlider stringValue]);
	return self;
}

@end
