
/* Generated by Interface Builder */

#import <daymisckit/daymisckit.h>
#import <gamekit/gamekit.h>
#import <stdio.h>
#import <stdlib.h>		/* malloc */

BOOL getBOOLPreference(const char *name, BOOL def)
{
	const char *tmpstr = NXGetDefaultValue ([NXApp appName], name);
	int temp = def;
	if (tmpstr) sscanf(tmpstr, "%d", &temp);
    if (temp) return YES;
	return NO;
}

int getIntPreference(const char *name, int min, int max, int def)
{
	int tempInt;
	const char *tmpstr = NXGetDefaultValue ([NXApp appName], name);
	if (!tmpstr) return def;
	else {
		sscanf(tmpstr, "%d", &tempInt);
		if (tempInt < min) return min;
		if (tempInt > max) return max;
	}
	return tempInt;
}

const char *getStringPreference(const char *name, const char *def)
{
	const char *tmpstr = NXGetDefaultValue ([NXApp appName], name);
	if (tmpstr == NULL) return def;
    return tmpstr; 
}

void putIntPreference(const char *name, int value)
{
	char str[16];
	sprintf(str, "%d", value);
	NXWriteDefault ([NXApp appName], name, str);
}

void putBOOLPreference(const char *name, BOOL value)
{
	char str[16];
	int tempInt = 0;
	if (value) tempInt = 1;
	sprintf(str, "%d", tempInt);
	NXWriteDefault ([NXApp appName], name, str);
}

void putStringPreference(const char *name, const char *value)
{
	NXWriteDefault ([NXApp appName], name, value);
}

@implementation PreferencesBrain

static char *defaultServer = "*";  // very slow -- subclass should override!

- init		// designated initializer sets up game variables
{		// to sensible values.
    int i;
    
	[super init];
    for (i=0; i<MAXKEYS; i++) {	// get space to store default keypresses in
    	keys[i] = malloc(8);
    	defkey[i] = malloc(8);
    }
    numKeys = 5;
	strcpy(defkey[0], "a");
	strcpy(defkey[1], "z");
	strcpy(defkey[2], "l");
	strcpy(defkey[3], ";");
	strcpy(defkey[4], " ");
    defaultPlayerName = [[DAYString alloc] init];
    version = malloc(8);
	serverName = NXCopyStringBufferFromZone(defaultServer, [self zone]);
	firstLoad = YES;
    
    return self;
}

- appDidInit:sender	// forwarded by GameBrain
{	// Do all our basic init here
	if (!gameBrain) gameBrain = [NXApp delegate];
	if (!info) info = [gameBrain infoController];
	if (!scorePlayer) scorePlayer = [gameBrain scorePlayer];
	if (!soundPlayer) soundPlayer = [gameBrain soundPlayer];
	if (!highScoreTable)
		highScoreTable = [gameBrain highScoreController];
	if (!strings) strings = [gameBrain mainStrings];
	if (!gameScreen) gameScreen = [gameBrain gameScreen];
	[self readDefaults:self];
	[gameScreen getPreferences];
	return self;
}

// methods to get at instance variables
- (int)startLevel { return level; }	// returns starting level
- (int)speed { return speed; }
- (BOOL)border { return border; }  // return YES if gray border is on
- (BOOL)effects { return effects; }
- (BOOL)music { return music; }
- (BOOL)alert { return alert; }
- (BOOL)autoUnPause { return autoUnPause; }
- (BOOL)autoStart { return autoStart; }
- (BOOL)demoSound { return demoSound; }
- (BOOL)useServer { return useServer; }
- setAlert:(BOOL)al { alert = al; return self; }
- setAutoUnPause:(BOOL)al { autoUnPause = al; return self; }
- setAutoStart:(BOOL)al { autoStart = al; return self; }
- (const char *)serverName { return serverName; }
- setNumKeys:(int)num { numKeys = num; return self; }

- readDefaults:sender		// get preferences from defaults database
{
	char *tmpstr = malloc(16);
    int i;
	
	if (serverName) free(serverName);
	useServer	= getBOOLPreference("UseServer", NO);
	effects		= getBOOLPreference("SoundOn", YES);
	alert		= getBOOLPreference("Alerts", YES);
	autoUnPause	= getBOOLPreference("AutoUnPause", YES);
	autoStart	= getBOOLPreference("AutoStart", YES);
	border		= getBOOLPreference("Border", YES);
	demoSound	= getBOOLPreference("DemoSound", YES);
	music		= getBOOLPreference("MusicOn", YES);
	level		= getIntPreference("StartLevel", 1, 20, 1);
	speed		= getIntPreference("GameSpeed", 0, 2, 1);
	
	serverName	= NXCopyStringBufferFromZone(
			getStringPreference("ServerName", defaultServer), [self zone]);
	strcpy(version, getStringPreference("Version", "0.0"));
	[self setDefaultPlayerName:getStringPreference(
			"PlayerName", [NXApp userRealName])];
	for (i=0; i < numKeys; i++) {
		sprintf(tmpstr, "Key%d", i);
		strcpy(keys[i], getStringPreference(tmpstr, defkey[i]));
		[gameScreen setKey:i val:keys[i][0]];    
	}
	
    [gameScreen getPreferences];
	[highScoreTable setServerHost:serverName];
	if (useServer) [highScoreTable setLocalScores:NO];
	else [highScoreTable setLocalScores:YES];
	// if couldn't locate the server, go local.
	if (useServer && [highScoreTable localScores]) useServer = NO;
	free(tmpstr);
	[soundPlayer turnOn:effects];
    return self;
}

- writeDefaults:sender		// save preferences in defaults database
{
	char str[32]; int i;
	
	putBOOLPreference	("Border",		border);
	putBOOLPreference	("Alerts",		alert);
	putBOOLPreference	("AutoStart",	autoStart);
	putBOOLPreference	("AutoUnPause",	autoUnPause);
	putBOOLPreference	("DemoSound",	demoSound);
	putBOOLPreference	("MusicOn",		music);
	putBOOLPreference	("SoundOn",		effects);
	putBOOLPreference	("UseServer",	useServer);
	putIntPreference	("GameSpeed",	speed);
	putIntPreference	("StartLevel",	level);
	putStringPreference	("Version",		version);
	putStringPreference	("ServerName",	serverName);
	putStringPreference	("PlayerName",	[defaultPlayerName stringValue]);
	
	for (i=0; i<numKeys; i++) { // ***** it would be better to make a single
		// string for all the keys; in the future I might make this adjustment.
		// This method leaves quite a few defaults...
		sprintf(str, "Key%d", i);
		putStringPreference(str, keys[i]);
	}
	return self;
}

- (BOOL)firstTimeCheck // first time running this version?
{
	if (!version || ![info versionString]) return NO; // if NULL, then this
		// mechanism is disabled, so it's NEVER the first time running...
	if (strcmp(version, [info versionString])) { // first time if !=
		strcpy(version, [info versionString]);
		return YES; // when the Prefs are saved (exit, etc.) the version
		// # will be stored in defaults and then next time we'll return a NO
	}
	return NO;
}

- revert:sender	// return to default values
{
    int tempInt;

	speed = 1; effects = YES; alert = YES;
	autoUnPause = YES; autoStart = YES; border = YES;
	demoSound = YES; music = YES; useServer = NO;
	if (serverName) free(serverName);
	serverName = NXCopyStringBufferFromZone(defaultServer, [self zone]);
	if (![highScoreTable localScores]) [highScoreTable closeServers];
	[highScoreTable setServerHost:serverName];
	[gameScreen revertBackground:sender];
	for (tempInt=0; tempInt<numKeys; tempInt++) {
		strcpy(keys[tempInt], defkey[tempInt]);
	}
	// I'm not reverting defaultPlayerName; doesn't seem necessary
    
	for (tempInt=0; tempInt<4; tempInt++)
		[gameScreen setKey:tempInt val:keys[tempInt][0]]; 
	[gameScreen getPreferences];   // make sure that the gameScreen is aware
	return [self preferences:self]; // update the panel
}

- takeKey:sender		   // used by below (keyChange) to get new keys
{ // This is klunky; there's a better way to fill up the matrix; I'll
	// fix it eventually ***** (See how the GKHighScorePanel does it; it's
	// much cleaner than this.)
    strcpy(keys[[sender tag]],[sender stringValue]);	// store internally
    [gameScreen setKey:[sender tag] val:keys[[sender tag]][0]]; // send to view
    return self;
}

- (char)keyVal:(int)keyIndex	// returns default key for action (0-4)
{ return keys[keyIndex][0]; }

- keyChange:sender			// accept changes in keys
{
    [keyMatrix sendAction:@selector(takeKey:) to:self forAllCells:YES];
	// ***** I should check here to be sure that the new keys don't
	// conflict with each other or with the 'p' and 'n' keys...
    return self;
}

- changeLevel:sender
{
    level = [sender intValue];
	if (sender != levelSlider) [levelSlider setIntValue:level];
	else [levelText setIntValue:level];
    return self;
}

- speedChange:sender			// accept changes in speed
{
    speed = [sender selectedTag];
    return self;
}


- findKeyFor:sender		// used by below to set keys in pref. matrix
{
    [sender setStringValue:keys[[sender tag]]];
    return self;
}

- setUpViews
{	// here is where we hook up the views to be switched around in the panel
	// you should override this to add extra panes besides the three basic
	// ones expected here; you can use Columns as an example of how you
	// would go about doing this.
	[inspectorBox getFrame:&inspectFrame];
	[viewPlayer getFrame:&view[0]];
	[viewScreen getFrame:&view[1]];
	[viewSound  getFrame:&view[2]];
	ViewsList = [[List alloc] init];
	thePopUpList = [thePopUpListButton target];
	[thePopUpList setTarget:self];
	[thePopUpList setAction:@selector(toggleInspectorPanels:)];
	[thePopUpList removeItemAt:0];  
	[thePopUpList removeItemAt:0];  
	[thePopUpList removeItemAt:0];  	
	[[[[self
		addView:viewPlayer withName:[strings valueForStringKey:"Player"]]
		addView:viewScreen withName:[strings valueForStringKey:"Screen"]]
		addView:viewSound  withName:[strings valueForStringKey:"Sound" ]]
		show:[strings valueForStringKey:"Player"]];
	[preferencesPanel orderFront:self];
	[inspectorBox display];
	return self;
}

- preferences:sender		// sets controls on pref panel & brings it up
{
    [self preferencesPanel]; // be sure panel is loaded; throw away return val.
	if (firstLoad) {
		[self setUpViews];
		[preferencesPanel orderFront:self];
		[inspectorBox display];
		firstLoad = NO;
	}
	[self refresh];
    [[self preferencesPanel] orderFront:self];
    return self;
}

- refresh	// refresh all controls -- set them to display the current state
{
	[speedButtons selectCellWithTag:speed];
    [musicButtons selectCellWithTag:music];
    [effectButtons selectCellWithTag:effects];
    [levelText setIntValue:level];
    [levelSlider setIntValue:level];
    [alertSwitch setState:alert];
    [borderSwitch setState:border];
    [serverButton setState:useServer];
    [serverText setStringValue:serverName];
    [autoStartSwitch setState:autoStart];
    [autoUnPauseSwitch setState:autoUnPause];
    [demoSwitch setState:demoSound];
    [keyMatrix sendAction:@selector(findKeyFor:) to:self forAllCells:YES];
	return self;
}

//
// Methods to handle inspectors and swapping views around...
// 	(Very much adapted from NewInspector.m)
//

- free
{
	[ViewsList free];
	[preferencesPanel orderOut:self];
	return [super free];
}

- addView:(id)aView withName:(const char *)name
{
	[ViewsList addObject:aView];
	[thePopUpList addItem:name];
	return self;
}

- show:(const char *)name
{
	[self turnOnView:[thePopUpList indexOfItem:name]];
	[thePopUpListButton setTitle:name];
	return self;
}

- toggleInspectorPanels:sender
{	// from pop-up list; we display the appropriate view.
	return [self turnOnView:[sender selectedRow]];
}

- turnOnView:(int)i
{
	NXRect newFrame;
	
	[preferencesPanel disableFlushWindow];
	// get the right view
	[inspectorBox setContentView:[ViewsList objectAt:i]];
	
	// resize and move to the right place
	newFrame.size.width = view[i].size.width;
	newFrame.size.height = view[i].size.height;
	newFrame.origin.x = inspectFrame.origin.x +
		(inspectFrame.size.width - view[i].size.width)/2;
	newFrame.origin.y = inspectFrame.origin.y +
		(inspectFrame.size.height - view[i].size.height)/2;
	[inspectorBox setFrameFromContentFrame:&newFrame];
	
	// erase junk around it -- this wouldn't be necessary if I
	// properly added/removed the Views from the respective hierarchies.
	// sometime I'll get around to fixing this.
	newFrame.size.width = view[i].size.width;
	newFrame.size.height = view[i].size.height;
	newFrame.origin.x = inspectFrame.origin.x +
		(inspectFrame.size.width - view[i].size.width)/2;
	newFrame.origin.y = inspectFrame.origin.y +
		(inspectFrame.size.height - view[i].size.height)/2;
	[grayView aroundFrame:&newFrame :inspectFrame.origin.y];
	
	// finally, display the new view
	[inspectorBox display];
	[self refresh];
	[preferencesPanel reenableFlushWindow];
	[preferencesPanel flushWindowIfNeeded];
	return self;
}

- preferencesPanel	// return the preferencesPanel, load it if needed.
{
	if( !preferencesPanel) {
		[NXApp loadNibSection:"PreferencesPanel.nib" owner:self withNames:NO];
		[preferencesPanel setFrameUsingName:"Prefs"];
		[preferencesPanel setFrameAutosaveName:"Prefs"];
	}
	return preferencesPanel;
}

- musicChange:sender		// turn music on/off
{
    music = [[sender selectedCell] tag];
	// music runs independently, so we just turn it on or off at the
	// appropriate times.  *****Maybe I should have the score playback
	// pause when you pause a game; right now it doesn't.
#ifdef GK_USE_MUSICKIT
    if ((music) && ([gameScreen gameState] != GAMEOVER))
		[scorePlayer play:self];
    else [scorePlayer stop:self];
#endif
    return self;
}

- setScore:sender
{	// sent by "Set Score File..." button; we forward to the ScorePlayer
#ifdef GK_USE_MUSICKIT
	[scorePlayer selectFile:sender];
#endif
	return self;
}

- effectsChange:sender		// turn sound effects on/off
{
	effects = [[sender selectedCell] tag];
	[soundPlayer turnOn:effects];
	return self;
}

- setDefaultPlayerName:(const char *)aString	// change default player name
{
	if (aString) [defaultPlayerName setStringValue:aString];
	else [defaultPlayerName setStringValue:"Mystery Player"];
    return self;
}

- (const char *)defaultPlayerName  // return ptr to defaultPlayerName string
{
    return [defaultPlayerName stringValue];
}

- alertChange:sender
{
    alert = NO;
    if ([sender state]) alert = YES;
    return self;
}

- unPauseChange:sender
{
    autoUnPause = NO;
    if ([sender state]) autoUnPause = YES;
    return self;
}

- autoStartChange:sender
{
    autoStart = NO;
    if ([sender state]) autoStart = YES;
    return self;
}

- demoSoundChange:sender	// New turn off sound only in demo
{
    demoSound = NO;
    if ([sender state]) demoSound = YES;
    return self;
}
 
- setBack:sender		// set the background image
{ // forward the message to the game screen
    return [gameScreen changeBackground:sender];
}

- revertBack:sender		// revert to default background image
{ // forward the message to the game screen
    return [gameScreen revertBackground:sender];
}

- (BOOL)borderOn { return border; }
- setBorder:(BOOL)newBord
{   // turn gray border on/off
    if (border == newBord) return self;
    border = newBord;
    [gameScreen changeBorder:border]; // inform the GameView to adjust things.
    return self;
}

- borderChange:sender
{   // turn gray border on/off -- sent by control on the panel
	// this is a cover for the above method
    return [self setBorder:[sender state]];
}

- setUseServer:(BOOL)useIt
{	// This allows the high score controlling object to turn things off
	// if it can't connect or it loses the connection to the server
	useServer = useIt;
	[serverButton setState:useIt];
	return [self refresh];
}

- changeServer:sender
{	// The main if block handles the button/check box, while the else
	// block handles changing the server name (and also automatically
	// checks the server box)
	if (sender != serverText) { // user changed the button state
		BOOL newUse = NO;
		// get the state of the check box
		if ([serverButton state]) newUse = YES;
		if (useServer != newUse) { // turn local on/off
			// attempt to connect to the servers if we aren't already
			// note that this call only will happen if we're going local
			// to network; if going the other way, we're already connected
			// so we pass through...
			if (![highScoreTable connected]) [highScoreTable connectToServers];
			// before finishing, make sure there were no problems connecting;
			// we can't assume all is OK.  (The -connected handles that; the
			// 0 is in the case of going to a local table...)
			useServer = (newUse ? [highScoreTable connected] : 0);
			[highScoreTable setLocalScores:!useServer];
		}
	} else {
		// check to see if the name is changing
		if (strcmp(serverName, [serverText stringValue])) {
			// make note of the new name (it has changed)
			if (serverName) free(serverName);
			serverName = NXCopyStringBufferFromZone([serverText stringValue],
					[self zone]);
			[highScoreTable setServerHost:serverName];
			// close current servers if already connected
			// because we need to switch to a different server
			if ([highScoreTable connected]) [highScoreTable closeServers];
		}
		// force us to use the server (i.e. return in server field will cause
		// the check box to be checked automatically, enabling the server.)
		useServer = YES;
		[serverButton setState:useServer];
		if (![highScoreTable connected]) [highScoreTable connectToServers];
		// refresh the high score panel amongst other things.
		[highScoreTable setLocalScores:![highScoreTable connected]];
	}
	// Note that if we couldn't locate the server, we go local.
	// (The HighScoreController did that automatically for us above.)
	// Finally, refresh the prefs panel to reflect any changes of internal
	// state, in particular the state of the check box, which may have
	// changed due to what happened above.  (No connection, etc.)
	[self refresh];
	return self;
}

// if the Prefs are "unfair" then a game cannot be saved on a network server.
// it is up to you to add methods that set unfair when the prefs become
// unfair.  Also, in -startingGame, you should set unfair if any prefs are
// unfair to begin with.  As it stands here, any game is fair...  Note that
// the only way to clear the unfair variable is by starting a new game with
// fair prefs; once a pref is made unfair, the game is unfair even if you
// change it back.
- (BOOL)unfair { return unfair; }
- setUnfair { unfair = YES; return self; }
- startingGame	// reset the unfair variable
{
	unfair = NO;
	return self;
}


@end
