
/* Generated by Interface Builder */

#import <gamekit/gamekit.h>		// Colors
#import <daymisckit/daymisckit.h>	// ExtendedApp, etc.
#import <dpsclient/psops.h>		// PSsetgray()
#import <dpsclient/wraps.h>		// PScompositerect()
#import <libc.h>				// for random(), etc.
#import <math.h>				// for floor(), etc.
#import <stdio.h>				// for standard C file operations


@implementation GameView

#define dragdef const char *dragTypes[3] =\
		{NXFilenamePboardType, NXColorPboardType, NULL}
#define dragNum	2

- initFrame:(const NXRect *)frm    // designated initializer for a view
{
	dragdef;
	
	[super initFrame:frm];
	cycles = 0;
	srandom(time(0));
	demoMode = NO;
	grayBorder = YES;
	state = GAMEOVER;
	demoWait = 0;
	doingBorder = NO;
	grayBorder = YES;
	backColor = NXConvertRGBToColor(0.333, 0.333, 0.333); // dark gray default
	backIsColor = YES; // default bg is dark gray...
	[self registerForDraggedTypes:dragTypes count:dragNum];
	dragOperation = NX_DragOperationNone;
	return self;
}

- appDidInit:sender		// forwarded by gamebrain
{
	if (!controller) controller = [NXApp delegate];
	if (!preferences) preferences = [controller preferencesBrain];
	if (!scoreKeeper) scoreKeeper = [controller scoreKeeper];
	if (!strings) strings = [controller mainStrings];
	if (!customSound) customSound = [controller soundPlayer];
	[self loadPix];
	return self;
}

- loadPix
{
	const char *tmpStr;
	char *okStr; float r, g, b;
	
	[self allocateGState];
	
// set background
	okStr = malloc(8);
	tmpStr = NXGetDefaultValue([NXApp appName], "BackColor");
	if (tmpStr) {
		backIsColor = NO;
		sscanf(tmpStr, "%s %f %f %f", &okStr[0], &r, &g, &b);
		backColor = NXConvertRGBToColor(r, g, b); 
		if (okStr[0] == 'Y') {
			backIsColor = YES;
		}
	}
	free(okStr);
	
	// get some buffers to hold our drawings.
	buffer = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size]; 
	staticBuffer = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size];
	 
    if (!backIsColor)
		[self setBackgroundFile:
				NXGetDefaultValue([NXApp appName], "BackGround")
			andRemember:NO];
    
	return self;
}

- animate:sender
{		// method to start the animation - called by appDidInit

    // set up Animator: (autoUpdate)  this handles moving viruses and
    //   makes sure the columns fall when they should.  The animator is
    //   an object that handles timed entries "smartly".
    animator = [[Animator alloc] initChronon:[self speedTime]
    		adaptation:0.0   target:self
    		action:@selector(autoUpdate:) autoStart:YES eventMask:0];
    
    return self;
}

- (BOOL)demoMode			// if last game was demo
{ return demoMode; }

- (float)speedTime		// returns time between animation frames
{  return GRANULARITY;	// return delay between clock ticks
}			// 0.05 is about as fast as should be used.

- (int)gameState	// tell caller our state... if in demo, game is over
{ if (demoMode) return GAMEOVER; return state; }

- autoUpdate:sender	// called by timed entry to update screen periodically
{			// ALL animation is controlled from here!!!
    cycles++;
	// you would do updates to various screen objects and
	// then do a [self updateSelf:&bounds :1];
    return self;
}

- updateSelf:(NXRect *)rects :(int)rectCount	// redraws the screen.
{		// it redraws only what has changed since last redraw.
	NXPing();
	return self;
}

- mouseDown:(NXEvent *)event		// handle mouseDown events.
{
    [controller pauseGame:self];	// pause/unpause game
    return self;
}

- (BOOL)acceptsFirstMouse		// let us grab activating mousedowns
{	// 1st click is to become 1st responder, NOT pause/unpause
    return NO;
}

- setKey:(int)keyIndex val:(char)keyVal		// change key we respond to
{
    keys[keyIndex] = keyVal;
    return self;
}

- pause:sender				// set pause status.  
{
    paused = YES;
    return self;
}

- unpause:sender			// remove paused status
{
    paused = NO;
    return self;
}
	
- (BOOL)isPaused			// tell caller is we're paused
{ return paused; }

- keyDown:(NXEvent *)myevent		// handle keyDown events.
{
	PSobscurecursor();  // subclasses which override need to do this.
	if (myevent->data.key.charSet == NX_ASCIISET &&
		(myevent->flags&(NX_CONTROLMASK|NX_ALTERNATEMASK|NX_COMMANDMASK)) == 0)
	{	
		if (GKKEYCODE == 'p') { // allow pause
			if (paused) [controller unpause];
			else [controller pause];
		} else if (GKKEYCODE == 'n') { // allow new game
			[controller startNewGame:self]; // re-start the game
			// returns nil if user changes mind...
		} else if (paused) {	// any keyDown unpauses game (except 'p').
			[controller unpause];
		}
	} else return [super keyDown:myevent];
    return self;
}

- (BOOL)acceptsFirstResponder		// to grab keyboard events
{ return YES; }

- getPreferences
{
	if (!preferences) preferences = [[NXApp delegate] preferencesBrain];
    return self;
}

- free					// get rid of support objects
{
    [animator free];
    return [super free];
}

- setUpScreen
{	    
    if ((demoMode)||(doingBorder)) [self display];
    return self;
}

- changeBorder:(BOOL)borderOn
{ // move view about in the window & do sizing
    NXRect wFrame;
	
	if (borderOn == grayBorder) return self;
	
	if (borderOn) {
		[window getFrame:&wFrame];
		NXInsetRect(&wFrame, -BEZELSIZE, -BEZELSIZE);
		[self moveBy:BEZELSIZE :BEZELSIZE];
		[window placeWindowAndDisplay:&wFrame];
	} else {
		[window getFrame:&wFrame];
		NXInsetRect(&wFrame, BEZELSIZE, BEZELSIZE);
		[self moveBy:-BEZELSIZE :-BEZELSIZE];
		[window placeWindowAndDisplay:&wFrame];
	}
	grayBorder = borderOn;
	return self;
}

- restartGame
{
	cycles = 0;
    state = NORMALSTATE;
    if (demoMode) {
		demoMode = NO;
		[[self window] setTitle:[strings valueForStringKey:"GameName"]];
    }
    [scoreKeeper resetScore];
    [self getPreferences]; // make sure we're up to date
    return self;
}

//// Background handling methods stolen from BreakApp:

- setBackgroundFile:(const char *)fileName andRemember:(BOOL)remember
// This methods allows changing the file used to paint the background of the
// playing field. Set fileName to NULL to revert to the default. Set
// remember to YES if you wish the write the value out in the defaults.
{
	[backGround free];

	backGround = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size];
	[backGround setBackgroundColor:backColor];
	[backGround setScalable:YES];
	if (fileName) {
		[backGround useFromFile:fileName];
		if (remember) {
			NXWriteDefault ([NXApp appName], "BackGround", fileName);
		}
	} else { // default background image
		[backGround useFromSection:"BackGround.eps"];
		if (remember) {
			NXRemoveDefault ([NXApp appName], "BackGround");
		}
	}
	backIsColor = NO;
	[self buildBackground];
	[self writeColor];
	[self display];
	return self;   
}

- buildBackground { return self; }	// for subclass use

// The following two methods allow changing the background image from
// menu items or buttons.

- changeBackground:sender
{
	const char *const *types = [NXImage imageFileTypes];  
	if ([[OpenPanel new] runModalForTypes:types]) {
		[self setBackgroundFile:[[OpenPanel new] filename] andRemember:YES];
		[self display];
	}
	return self;
}

- revertBackground:sender
{
	backIsColor = NO;
    [self setBackgroundFile:NULL andRemember:YES];
    [self display];
    return self;
}

- loadAnImage:(const char *)imageName
{
    char *tempStr = (char *)malloc(MAXPATHLEN);
	sprintf(tempStr, "%s/%s", [NXApp appDirectory], imageName);
    [self setBackgroundFile:tempStr andRemember:YES];
    free(tempStr); [self display];
    return self;
}

- back1:sender { return [self loadAnImage:"Back1.eps"]; }
- back2:sender { return [self loadAnImage:"Back2.tiff"]; }
- back3:sender { return [self loadAnImage:"Back3.tiff"]; }

- drawBackground:(NXRect *)rect
// drawBackground: just draws the specified piece of the background by
// compositing from the background image.
{
    NXRect tmpRect = *rect;

	if (backIsColor) {
		NXSetColor(backColor);
		NXRectFill(rect);
		return self;
	}
	
	NX_X(&tmpRect) = floor(NX_X(&tmpRect));
	NX_Y(&tmpRect) = floor(NX_Y(&tmpRect));
	if (NXDrawingStatus == NX_DRAWING) {
		PSsetgray (NX_BLACK);
		PScompositerect (NX_X(&tmpRect), NX_Y(&tmpRect),
			NX_WIDTH(&tmpRect), NX_HEIGHT(&tmpRect), NX_SOVER);
	}
	[backGround composite:NX_SOVER fromRect:&tmpRect toPoint:&tmpRect.origin];
	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height 
{
    [super sizeTo:width :height];
    [backGround setSize:&bounds.size];
    return self;
}


// deal with drag and drop colors:
- acceptColor:(NXColor)color atPoint:(const NXPoint *)aPoint
{
	backIsColor = YES;
	backColor = color;
	[[self writeColor] update];
	return self;
}

- writeColor
{
	char def[256];
	float r, g, b;
	
	NXConvertColorToRGB(backColor, &r, &g, &b);
	sprintf(def, "%s %f %f %f", (backIsColor ? "YES" : "NO"), r, g, b);
	NXWriteDefault ([NXApp appName], "BackColor", def);
	return self;
}


// these assume that you already locked focus.  Thus, when overriding
// these methods, the subclas should lock focus in the static buffer
// before calling the super (this) method.
- rebuildStaticBuffer
{
	[self drawBackground:&bounds];
	return self;
}

- rebuildStaticAt:(NXRect *)rect
{
	[self drawBackground:rect];
	return self;
}

// drag and drop an image into the view's background.
// uses 3.0 drag kit stuff

- (NXDragOperation)draggingEntered:sender
{
	NXDragOperation sourceMask;

	// Ask the sender for its operation mask.
	sourceMask = [sender draggingSourceOperationMask]; 
	if (sourceMask & NX_DragOperationCopy)
		dragOperation = NX_DragOperationCopy;
	else if (sourceMask & NX_DragOperationGeneric)
		dragOperation = NX_DragOperationGeneric;
	else dragOperation = NX_DragOperationNone;
	return dragOperation;
}	

- (NXDragOperation)draggingUpdated:sender { return dragOperation; }
- draggingExited:sender { return self; }

- (BOOL)prepareForDragOperation:sender
{ // We do want to accept the dragged image/color.
	return YES;
}
	
- (BOOL)performDragOperation:sender
{
	Pasteboard *dragPasteboard = [Pasteboard newName:NXDragPboard];
	char *file;
	int length;
	dragdef;
	BOOL foundOne = NO;

	if ([dragPasteboard findAvailableTypeFrom:dragTypes num:dragNum]
			== NXFilenamePboardType) {
		if ([dragPasteboard readType:NXFilenamePboardType
							data:&file length:&length]) {
			foundOne = YES; 
			[self setBackgroundFile:file andRemember:YES];
			[dragPasteboard deallocatePasteboardData:file length:length];
			[NXApp activateSelf:YES];
			[window makeKeyAndOrderFront:self];
	}	}
	else if ([dragPasteboard findAvailableTypeFrom:dragTypes num:dragNum]
			== NXColorPboardType) {
		NXPoint p = [sender draggingLocation];
		NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
		[self acceptColor:c atPoint:&p];
		foundOne = YES;
		[NXApp activateSelf:YES];
		[window makeKeyAndOrderFront:self];
	}
	return foundOne;
}
	
- concludeDragOperation:sender { return self; }

- gameOver	// sent by GameBrain
{
	state = GAMEOVER;
	return self;
}

@end
