/*
 *  AudioUImockup.mm
 *  AudioUIdemo
 *
 *	Code and Sound Design by Peter "pdx" Drescher.
 *  Copyright 2010 twittering machine. All rights reserved.
 * 
 *	This is the main application control module.
 *	It contains all the interface, screen display, and menu creation code.
 *	First, a "terminal mockup" screen is displayed, and the default set of sounds is loaded.
 *	When the user types on the keyboard, a pointer to the audioFileArray is selected for each keyDown event,
 *	according to the current algorithm, and a sound is played for each character displayed.
 *	If the user types in one of a series of keywords, a response string is generated,
 *	which is then "printed" to the screen by generating a series of keyDown events and posting them to
 *	the event queue. Events then get played back at the selected speed, using the current sound set
 *	and algorithm. Flashing error alerts will also be generated for the (google) "search" keyword,
 *	if no doucments are found, or the computer is disconnected from the Internet.
 *
 */

#import "AudioUImockup.h"
#import "AudioFileArray.h"
#import "ScreenPrint.h"

// menu setting definitions
#define SOUND_BEEP			56600
#define SOUND_BLIP			56610
#define SOUND_NOISE			56620
#define SOUND_TELEMETRY		56630
#define SOUND_CHIRP			56640
#define SOUND_KEYBOARD		56650
#define SOUND_CHROMATIC		56660
#define SOUND_DIATONIC		56670

#define ALG_MONOTONE		56700
#define ALG_WHITE			56710
#define ALG_PINK			56720
#define ALG_BROWN			56730
#define ALG_WEIGHTED_ALPHA	56740
#define ALG_WEIGHTED_QWERTY	56750
#define ALG_WEIGHTED_ETAOIN	56760

#define PRINT_SPEED_SLOW	200000
#define PRINT_SPEED_MEDIUM	100000
#define PRINT_SPEED_FAST	50000
#define PRINT_SPEED_ZIP		25000

#define ROW_HEIGHT			25
#define CHAR_WIDTH			15
#define LEFT_BORDER			10

#define WHITE_ALERT			1
#define	RED_ALERT			2

//NSInteger currentSoundSetting;
//NSInteger currentAlgorithmSetting;
//NSInteger currentSpeedSetting;
//int	arrayPos;

ScreenPrint sPrint;

@implementation AudioUImockup

- (void)prepareAttributes
{
	attributes = [[NSMutableDictionary alloc] init];
	[attributes setObject:[NSFont fontWithName:@"Courier"
										  size:20]
				   forKey:NSFontAttributeName];
	
	[attributes setObject:[NSColor greenColor]
				   forKey:NSForegroundColorAttributeName];
	 
}

- (id)initWithFrame:(NSRect)frame
{
    [super initWithFrame:frame];
	[self prepareAttributes];
	bgColor = [[NSColor blackColor] retain];
	terminalScreenHeight = frame.size.height;
	terminalScreenWidth = frame.size.width;
	currentPosition.x = frame.origin.x + LEFT_BORDER;
	currentPosition.y = terminalScreenHeight - ROW_HEIGHT;

	clearScreen = YES;
	firstScreen = YES;
	firstChar = YES;
	shKey = NO;
	scrPrinting = NO;
	inString = @">initialize";
	
	initSoundAlgorithm ();
	audioFileArray = [[AudioFileArray alloc]init]; // this is important!
	sPrint.init();
	arrayPos = 20; //middle of the array, default starting place

	return self;
}

- (void)dealloc
{
	[bgColor release];
	[inString release];
	[attributes release];
	[audioFileArray release];
	[super dealloc];
}

#pragma mark Drawing

- (BOOL)isOpaque
{
	return YES;
}

/*
*	Draw the character contained inString at the correct place on the terminal screen,
*	resetting position if far right, or bottom, of screen reached.
*	Also, play a sound for character, if no alarm is currently running (which gets it's own sound).
*/
- (void)printTheChar:(NSRect)r
{
	currentPosition.x += CHAR_WIDTH;
	if (currentPosition.x > (terminalScreenWidth - CHAR_WIDTH))
	{
		currentPosition.x =  LEFT_BORDER + CHAR_WIDTH;
		currentPosition.y -= ROW_HEIGHT;
		if (currentPosition.y < ROW_HEIGHT) {
			[NSBezierPath fillRect:r];
			currentPosition.y = r.size.height - ROW_HEIGHT;		
		}
	}
	
	if (alertCounter == 0) {
		[self setArrayPos];
		[audioFileArray playSoundAt:arrayPos];	
	}
	
	[inString drawAtPoint:currentPosition withAttributes:attributes];
}
				   
//	pick a position in the audioFileArray, depending on the current Algorithm
- (void)setArrayPos
{	
	switch (currentAlgorithmSetting) {
		case ALG_MONOTONE:
			arrayPos = 20;
			break;
		case ALG_WHITE:
			arrayPos = calcWhiteNoise();
			break;
		case ALG_PINK:
			arrayPos = calcPinkNoise();
			break;
		case ALG_BROWN:
			arrayPos = calcBrownian(arrayPos);
			break;
		case ALG_WEIGHTED_ALPHA:
			arrayPos = calcWeightedAlpha([inString characterAtIndex:0]);
			break;
		case ALG_WEIGHTED_QWERTY:
			arrayPos = calcWeightedQwerty([inString characterAtIndex:0]);
			break;
		case ALG_WEIGHTED_ETAOIN:
			arrayPos = calcWeightedETA([inString characterAtIndex:0]);
			break;
		default:
			break;
	}
}

/*	
*	refreshes the terminal screen
*	if the "clear" flag is set, erase the screen and set the print position to upper left corner
*	don't print the '\f' line feed character
*	but do initialize the screen if it is being displayed for the first time
*/
- (void)drawRect:(NSRect)rect 
{
	if (clearScreen)
	{
		NSRect bounds = [self bounds];
		[bgColor set];
		[NSBezierPath fillRect:bounds];
		currentPosition.x = bounds.origin.x + LEFT_BORDER;
		currentPosition.y = bounds.size.height - ROW_HEIGHT;
		clearScreen = NO;
	}

	if ([inString characterAtIndex:0] != '\f') {
		[self printTheChar:rect];
	}
	
	if (firstScreen) {
		currentPosition.x =  LEFT_BORDER + CHAR_WIDTH;
		currentPosition.y -= ROW_HEIGHT;
		[self setInString:@">"];
		[inString drawAtPoint:currentPosition withAttributes:attributes];
		firstScreen = NO;
	}
}

#pragma mark Keyboard Events

- (BOOL)acceptsFirstResponder
{
	NSLog(@"Accepting");
	return YES;
}

/*
*	receives a keyDown event from the system queue.
*	if in "screen printing" mode, the events have been generated,
*	and so the rate of display is regulated by the current speed setting
*	also, get the necessary data for subsequent event generation (window number, etc) from the first entry
*/
- (void)keyDown:(NSEvent *)e
{	
	if (scrPrinting) {
		usleep(currentSpeedSetting);
	}
	
	if ([e modifierFlags] & NSShiftKeyMask) {shKey = YES;} else {shKey = NO;}
	[self interpretKeyEvents:[NSArray arrayWithObject:e]];
	
	if (firstChar) {
		scrPrintEvent = *e; //initialize screen print keydown event 
		firstChar = NO;
	}
}

/*
*	user typed a character, or a character was screen printed.
*	'\f' is interpreted during screen printing as a "line feed" (as opposed to a '\n' or '\r' "carriage return").
*	if at the bottom, clear the screen and start at the top (i.e. no scrolling in this version).
*	keep track of what the user entered for keyword recognition later.
*/
- (void)insertText:(NSString *)s
{
	[self setInString:s];
	unichar ch1 = [s characterAtIndex:0];
	if (ch1 == '\f') {
		[self doLineFeed];
	}
	
	if (currentPosition.y < ROW_HEIGHT)	{
		clearScreen = YES;
	}	
	
	if (!scrPrinting) {
		sPrint.appendCharToTypedIn(ch1);
	}
}

/*
*	user pressed "return", or screen printing "return" event placed in queue.
*	when in screen printing mode, a '\r' character indicates "end of keyword response" (so stop screen printing).
*	otherwise, check to see if the user entered a keyword:
*		if the response is "clear" - clear the screen
*		if the reponse is an "alert", process the flashing alert
*		otherwise, generate and post a keyDown event for each character in the generated keyword response
*/
- (void)insertNewline:(NSString *)s
{
	[self doLineFeed];
	if (scrPrinting)
	{
		[self setInString:@">"];
		scrPrinting = NO;
	}
	else
	{
		if (sPrint.checkForKeyword() != "")
		{
			scrPrinting = YES;
			NSLog(@"sPrint.keyWordResponse = %s", sPrint.keyWordResponse.c_str());
			NSString *kwrStr = [NSString stringWithUTF8String:sPrint.keyWordResponse.c_str()];
			
			if ([kwrStr isEqualToString: @"clear\r"]) {
				clearScreen = YES;
				[audioFileArray playClearSound];
				kwrStr = @"\r";
			}
			
			if ([kwrStr isEqualToString: @"alert1\r"]) {
				alertType = WHITE_ALERT;
				[self doFlashingError];
				if (currentPosition.y > ROW_HEIGHT * 2) { // edge case for last and penultimate line on screen
					kwrStr = @"\f";
				} else {
					kwrStr = @" ";
				}
			}
			
			if ([kwrStr isEqualToString: @"alert2\r"]) {
				alertType = RED_ALERT;
				[self doFlashingError];
				if (currentPosition.y > ROW_HEIGHT * 2) { // edge case for last and penultimate line on screen
					kwrStr = @"\f";
				} else {
					kwrStr = @" ";
				}
			}
			
			NSEvent *scrPrintChar;		
			for (int x = 0; x < [kwrStr length]; x++)
			{
				NSRange charAtX = NSMakeRange(x, 1);
				NSString *kwrChar = [kwrStr substringWithRange:charAtX];
				scrPrintChar = [NSEvent keyEventWithType: NSKeyDown
												 location:[&scrPrintEvent locationInWindow]
											modifierFlags:[&scrPrintEvent modifierFlags]
												timestamp:([&scrPrintEvent timestamp] + x)
											 windowNumber:[&scrPrintEvent windowNumber]
												  context:[&scrPrintEvent context]
											  characters: kwrChar
							 charactersIgnoringModifiers: kwrChar
												isARepeat: NO
												  keyCode:[&scrPrintEvent keyCode]];
			
				[NSApp postEvent:scrPrintChar atStart:NO];
			}
		}
		else
		{
			[self setInString:@">"];
		}
	}
	sPrint.typedIn = "";
}

- (void)deleteBackward:(id)sender
{
	sPrint.appendCharToTypedIn(0xFF);											// signals sPrint to erase the last letter typed in
	// edge cases
	if (currentPosition.x < (LEFT_BORDER + CHAR_WIDTH))							// first letter on line
	{
		if (currentPosition.y < terminalScreenHeight - ROW_HEIGHT)				// not at top of screen
		{ 
			currentPosition.x = terminalScreenWidth - LEFT_BORDER - CHAR_WIDTH; // jump to last letter (1075)
			currentPosition.y += ROW_HEIGHT;                                    // on previous line
		}
		else
		{
			currentPosition.y = terminalScreenHeight - ROW_HEIGHT;				// at top of screen
			currentPosition.x = LEFT_BORDER + (CHAR_WIDTH * 2);					// fake out position for ">" prompt
			[self setInString:@">"];
		}
	}
	else
	{
		[self setInString:@" "];												// use a space to erase the onscreen letter
	}
	
	if (shKey)																	//Shift+Delete = clear screen
	{
		[self setInString:@">"];
		clearScreen = YES;
		[audioFileArray playClearSound];
		shKey = NO;
	}
	else
	{																			// erase the letter and reset the current position
		NSRect bounds = [self bounds];
		bounds.origin = currentPosition;
		bounds.size.width = CHAR_WIDTH;
		bounds.size.height = ROW_HEIGHT;
		[NSBezierPath fillRect:bounds];
		[self setNeedsDisplay:YES];	
		currentPosition.x -= (CHAR_WIDTH * 2);
	}
}

- (void)doLineFeed
{
	currentPosition.x =  LEFT_BORDER;
	currentPosition.y -= ROW_HEIGHT;
	if ((currentPosition.y < ROW_HEIGHT) &&
		(alertCounter == 0))
	{
		clearScreen = YES;
	}	
}

/*
*	flash error alert on/off each second, setting the alert box to the current position (taking into account edge cases)
*/
- (void)doFlashingError
{
	alertCounter = 1;
	alertBox.origin.x = currentPosition.x + LEFT_BORDER;
	if (currentPosition.y < ROW_HEIGHT) {
		alertBox.origin.y = terminalScreenHeight - ROW_HEIGHT;
	} else {
		alertBox.origin.y = currentPosition.y;
	}
	alertBox.size.height = ROW_HEIGHT;
	alertTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
												 target:self
												selector:@selector(flashAlert:)
											   userInfo:nil
												repeats:YES];
	[alertTimer retain];
}

/*
*	flash the alert (and play the alert sound) 3 times, by drawing the alert string in an alternating colored/clear box
*	when completed, clear the alert and the timer
*/
- (void)flashAlert:(NSTimer*)t
{
	alertCounter++;
	CGFloat fillColor;
	if (alertCounter < 8) {
		if ((alertCounter % 2) == 0) {
			fillColor = 0.7;
			[audioFileArray playAlertSound:alertType];
		} else {
			fillColor = 0;
		}

		switch (alertType) {
			case WHITE_ALERT:
				[self setInString:@"Your search did not match any documents."];
				alertBox.size.width = CHAR_WIDTH * ([inString length] - 7);
				[[NSColor colorWithDeviceWhite:fillColor alpha: 1] setFill];
				break;
			case RED_ALERT:
				[self setInString:@"Not connected to Internet!"];
				alertBox.size.width = CHAR_WIDTH * ([inString length] - 4);
				[[NSColor colorWithDeviceRed:fillColor green:0 blue:0 alpha: 1] setFill];
				break;
			default:
				break;
		}
		currentPosition.x = alertBox.origin.x - LEFT_BORDER;
		currentPosition.y = alertBox.origin.y;
		[NSBezierPath fillRect:alertBox];
	} else {
		alertCounter = 0;
		[self doLineFeed];
		scrPrinting = NO;
		[self setInString:@">"];
		[alertTimer invalidate];
		[alertTimer release];
		alertTimer = nil;
	}
}

- (NSColor *)bgColor
{
	return bgColor;
}

- (void)setBgColor:(NSColor *)c
{
	[c retain];
	[bgColor release];
	bgColor = c;
	[self setNeedsDisplay:YES];
}

- (NSString *)inString
{
	return inString;
}

- (void)setInString:(NSString *)s
{
	s = [s copy];
	[inString release];
	inString = s;
	[self setNeedsDisplay:YES];
}

#pragma mark Menus

- (void)createSoundMenu {
	
    NSMenu *newMenu;
    NSMenuItem *newItem;
	
    // Add the submenu
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Sound" action:NULL keyEquivalent:@""];
    newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Sound"];
    [newItem setSubmenu:newMenu];
    [newMenu release];
    [[NSApp mainMenu] addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Beep" action:NULL keyEquivalent:@"a"];
    [newItem setTag:SOUND_BEEP];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Blip" action:NULL keyEquivalent:@"s"];
    [newItem setTag:SOUND_BLIP];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Noise" action:NULL keyEquivalent:@"d"];
    [newItem setTag:SOUND_NOISE];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Telemetry" action:NULL keyEquivalent:@"f"];
    [newItem setTag:SOUND_TELEMETRY];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Chirp" action:NULL keyEquivalent:@"g"];
    [newItem setTag:SOUND_CHIRP];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Keyboard" action:NULL keyEquivalent:@"h"];
    [newItem setTag:SOUND_KEYBOARD];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Chromatic" action:NULL keyEquivalent:@"j"];
    [newItem setTag:SOUND_CHROMATIC];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Diatonic" action:NULL keyEquivalent:@"k"];
    [newItem setTag:SOUND_DIATONIC];
    [newItem setTarget:self];
    [newItem setAction:@selector(soundAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
}

- (void)createAlgorithmMenu {
	
    NSMenu *newMenu;
    NSMenuItem *newItem;
	
    // Add the submenu
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Algorithm" action:NULL keyEquivalent:@""];
    newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Algorithm"];
    [newItem setSubmenu:newMenu];
    [newMenu release];
    [[NSApp mainMenu] addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Monotone" action:NULL keyEquivalent:@"1"];
    [newItem setTag:ALG_MONOTONE];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Random (White Noise)" action:NULL keyEquivalent:@"2"];
    [newItem setTag:ALG_WHITE];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"1/f (Pink Noise)" action:NULL keyEquivalent:@"3"];
    [newItem setTag:ALG_PINK];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Stochastic (Brownian)" action:NULL keyEquivalent:@"4"];
    [newItem setTag:ALG_BROWN];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Weighted: Alpha" action:NULL keyEquivalent:@"5"];
    [newItem setTag:ALG_WEIGHTED_ALPHA];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Weighted: Qwerty" action:NULL keyEquivalent:@"6"];
    [newItem setTag:ALG_WEIGHTED_QWERTY];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Weighted: ETAOIN SHRDLU" action:NULL keyEquivalent:@"7"];
    [newItem setTag:ALG_WEIGHTED_ETAOIN];
    [newItem setTarget:self];
    [newItem setAction:@selector(algAction:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
}

- (void)createPrintSpeedMenu {
	
    NSMenu *newMenu;
    NSMenuItem *newItem;
	
    // Add the submenu
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Print Speed" action:NULL keyEquivalent:@""];
    newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Print Speed"];
    [newItem setSubmenu:newMenu];
    [newMenu release];
    [[NSApp mainMenu] addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Slow" action:NULL keyEquivalent:@"1"];
    [newItem setTag:PRINT_SPEED_SLOW];
	[newItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
    [newItem setTarget:self];
    [newItem setAction:@selector(setPrintSpeed:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Medium" action:NULL keyEquivalent:@"2"];
    [newItem setTag:PRINT_SPEED_MEDIUM];
	[newItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
    [newItem setTarget:self];
    [newItem setAction:@selector(setPrintSpeed:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Fast" action:NULL keyEquivalent:@"3"];
    [newItem setTag:PRINT_SPEED_FAST];
	[newItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
    [newItem setTarget:self];
    [newItem setAction:@selector(setPrintSpeed:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
    newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Zip!" action:NULL keyEquivalent:@"4"];
    [newItem setTag:PRINT_SPEED_ZIP];
	[newItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
    [newItem setTarget:self];
    [newItem setAction:@selector(setPrintSpeed:)];
    [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
    [newMenu addItem:newItem];
    [newItem release];
	
}

//	make the menus, and set the defaults
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
    [self createSoundMenu];
    [self createAlgorithmMenu];
    [self createPrintSpeedMenu];

    currentSoundSetting = SOUND_BEEP;
    currentAlgorithmSetting = ALG_MONOTONE;
    currentSpeedSetting = PRINT_SPEED_FAST;
}

//	load the audioFileArray based on the sound set selected
- (void)soundAction:(id)sender {
    currentSoundSetting = [sender tag];
	switch (currentSoundSetting) {
		case SOUND_BEEP:
			[audioFileArray loadAudioArray:@"beep"];
			break;			
		case SOUND_BLIP:
			[audioFileArray loadAudioArray:@"blip"];
			break;			
		case SOUND_NOISE:
			[audioFileArray loadAudioArray:@"noise"];
			break;			
		case SOUND_TELEMETRY:
			[audioFileArray loadAudioArray:@"tele"];
			break;			
		case SOUND_CHIRP:
			[audioFileArray loadAudioArray:@"chirp"];
			break;			
		case SOUND_KEYBOARD:
			[audioFileArray loadAudioArray:@"key"];
			break;			
		case SOUND_CHROMATIC:
			[audioFileArray loadAudioArray:@"sq"];
			break;			
		case SOUND_DIATONIC:
			[audioFileArray loadAudioArray:@"mgtrD"];
			break;			
		default:
			break;
	}
}

//	set the color of the characters printed on screen, based on the algorithm type
- (void)algAction:(id)sender {
    currentAlgorithmSetting = [sender tag];
	switch (currentAlgorithmSetting) {
		case ALG_MONOTONE:
			[attributes setObject:[NSColor greenColor]		forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_WHITE:
			[attributes setObject:[NSColor whiteColor]		forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_PINK:
			[attributes setObject:[NSColor magentaColor]	forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_BROWN:
			[attributes setObject:[NSColor brownColor]		forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_WEIGHTED_ALPHA:
			[attributes setObject:[NSColor blueColor]		forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_WEIGHTED_QWERTY:
			[attributes setObject:[NSColor blueColor]		forKey:NSForegroundColorAttributeName];
			break;			
		case ALG_WEIGHTED_ETAOIN:
			[attributes setObject:[NSColor blueColor]		forKey:NSForegroundColorAttributeName];
			break;			
		default:
			break;
	}
}

- (void)setPrintSpeed:(id)sender {
    currentSpeedSetting = [sender tag];
}

//	sets the menu's radio button on or off as necessary
- (BOOL)validateMenuItem:(NSMenuItem *)item {
    NSInteger tag = [item tag];
	if (   (tag == SOUND_BEEP) 
		|| (tag == SOUND_BLIP) 
		|| (tag == SOUND_NOISE)
		|| (tag == SOUND_TELEMETRY)
		|| (tag == SOUND_CHIRP)
		|| (tag == SOUND_KEYBOARD)
		|| (tag == SOUND_CHROMATIC)
		|| (tag == SOUND_DIATONIC))
	{	[item setState:((tag == currentSoundSetting) ? NSOnState : NSOffState)];
		return YES;
	}
	else
	if (   (tag == ALG_MONOTONE)
		|| (tag == ALG_WHITE) 
		|| (tag == ALG_PINK) 
		|| (tag == ALG_BROWN) 
		|| (tag == ALG_WEIGHTED_ALPHA) 
		|| (tag == ALG_WEIGHTED_QWERTY) 
		|| (tag == ALG_WEIGHTED_ETAOIN))
	{	[item setState:((tag == currentAlgorithmSetting) ? NSOnState : NSOffState)];
		return YES;
	}
	else
	if (   (tag == PRINT_SPEED_SLOW)
		|| (tag == PRINT_SPEED_MEDIUM)
		|| (tag == PRINT_SPEED_FAST)
		|| (tag == PRINT_SPEED_ZIP))
	{	[item setState:((tag == currentSpeedSetting) ? NSOnState : NSOffState)];
		return YES;
	}
	else
		return NO;
}

@end
