/*
 * SplatApp
 * description: subclass of EKApplication - keeps track of the main UI
 *		components and application flow
 * history:
 *	5/1/93 [Erik Kay] - converted much of the SplatController to here
 *	6/8/93 [Erik Kay] - cleaned up some of the file opening code
 */


#import "SplatApp.h"
#import "SplatGame.h"
#import "SplatDefaults.h"
#import "Board.h"
#import "ComputerPlayer.h"
#import "HumanPlayer.h"
#import "NetworkPlayer.h"
#import <sys/dir.h>

#define COMP_EXTENSION "scplayer"

@implementation SplatApp

- challenge:sender
{
    int exitValue;
    id panel;
    DPSContext newcontext, oldcontext;
    NXHandler handler;
    newcontext = DPSCreateContext("drool",0,0,0);
    oldcontext = context;
    context = newcontext;
    DPSSetContext(newcontext);
    panel = NXGetAlertPanel
    	("Splat Challenge",
	"Erik is challenging you to a game of Splat.  Do you accept?",
	"OK","Cancel",NULL);
    [panel center];
    [panel makeKeyAndOrderFront:self];
    NX_DURING {
        handler.code = 0;
        exitValue = [NXApp runModalFor:panel];
    } NX_HANDLER {
        handler = NXLocalHandler;
        if (handler.code == dps_err_ps) NXReportError((NXHandler *)(&handler));
    } NX_ENDHANDLER
    [panel orderOut:self];
    context = oldcontext;
    DPSSetContext([NXApp context]);
    return self;
}

/*
 * setup and new game initialization
 */

+ new
{    
    self = [super new];
    [self setDelegate:self]; //? we're our own delegate... duh!
    
    // setup resources that I want to be preloaded
    [self addImageResource:"one"];
    [self addImageResource:"two"];
    [self addImageResource:"obstruction"];
    [self addImageResource:"smallone"];
    [self addImageResource:"smalltwo"];
    [self addSoundResource:"move"];
    [self addSoundResource:"change"];
    [self addSoundResource:"bigChange"];
    [self addSoundResource:"wowChange"];
    [self addSoundResource:"gameOver"];
    [self addSoundResource:"win"];
    
    return self;
}
 
- appDidInit:sender
{
    NXRect f1, f2;
    int i;
    id button, matrix, popup;
    char path[MAXPATHLEN], boardPath[MAXPATHLEN];
    const char *tmp;

    [defaultsManager loadDefaults];

    // load the computer players
    [self loadComputerStrategies];
        
    // board file initialization
    tmp = [[NXBundle mainBundle] directory];
    sprintf(path,"%s/boards",tmp);
    boardBundle = [[NXBundle alloc] initForDirectory:path];
    strcpy(boardPath,[defaultsManager boardPath]);
        
    // initialize the mini board
    [miniSplatBoard setScalable:YES];
    [miniSplatBoard setBoard:[Board newFromFile:boardPath]];
    
    // games
    games = [[List alloc] init];

    // load resources
    [self loadResources];
    
    // setup the "new game setup" panel    
    player1CurrentView = player1NewHuman;
    player2CurrentView = player2NewComputer;
    [player1NewGame getFrame:&f1];
    [player2NewGame getFrame:&f2];
    [player1CurrentView setFrame:&f1];
    [player2CurrentView setFrame:&f2];
    [[player1NewGame superview] replaceSubview:player1NewGame
    				with:player1CurrentView];
    [[player2NewGame superview] replaceSubview:player2NewGame
    				with:player2CurrentView];
    [player1CurrentView display];
    [player2CurrentView display];
    [newGamePanel flushWindow];
    
    //? initialize all of the popups properly
    //? This is a hack to workaround bug #35247
    //? basically, popup matrices aren't initialized properly (they don't have
    //? a selected cell unless the user selects something), so the we have to
    //? do the initialization manually.  Note that all popup connections are
    //? also being set equal to their matrices since all future operations will
    //? be with the matrices themselves.  the variable name is a little
    //? confusing for this and perhaps we should have additional variables for
    //? the matrices
    button = player1TypePopup;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player1TypePopup = matrix;
    
    button = player2TypePopup;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player2TypePopup = matrix;
    
    button = player1SearchDepth;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player1SearchDepth = matrix;
    
    button = player2SearchDepth;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player2SearchDepth = matrix;
    
    button = player1CompPopup;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player1CompPopup = popup;

    button = player2CompPopup;
    popup = [button target];
    matrix = [popup itemList];
    [matrix selectCellAt:[popup indexOfItem:[button title]] :0];
    player2CompPopup = popup;

    // add the different strategies to the popup list for new game selection
    for (i = 0; i < [computerStrategies count]; i++) {
    	[[player1CompPopup addItem: [[computerStrategies objectAt:i] strategyName]] setTag:i];
    	[[player2CompPopup addItem: [[computerStrategies objectAt:i] strategyName]] setTag:i];
    }
    player1CompPopup = [player1CompPopup itemList];
    player2CompPopup = [player2CompPopup itemList];
    
    [player1HumanName setStringValue:[defaultsManager defaultPlayerName]];
    [player2HumanName setStringValue:[defaultsManager defaultPlayerName]];
    
    [newGamePanel makeKeyAndOrderFront:self];
    
    return self;
}

- initializePlayersForGame:game
{
    id item, player, strategy, rules;
    int tag;
    
    rules = [game rules];
    item = [player1TypePopup selectedCell];
    tag = [item tag];
    switch (tag) {
    	case 0: /* Human */
	    player = [[HumanPlayer alloc] initWithRules:rules andPieceType:SQUARE_ONE];
	    [player setPlayerName:[player1HumanName stringValue]];
	    break;
	case 1: /* Computer */
	    strategy = [computerStrategies objectAt:[[player1CompPopup selectedCell] tag]];
	    player = [[strategy alloc] initWithRules:rules andPieceType:SQUARE_ONE];
	    [player setSearchDepth:[[player1SearchDepth selectedCell] tag]];
	    break;
	case 2: /* Network */
	default:
	    player = [[NetworkPlayer alloc] initWithRules:rules andPieceType:SQUARE_ONE];
	    [player setHostname:[player1NetHostname stringValue]];
	    break;
    }
    [game setPlayer1:player];
    
    item = [player2TypePopup selectedCell];
    tag = [item tag];
    switch (tag) {
    	case 0: /* Human */
	    player = [[HumanPlayer alloc] initWithRules:rules andPieceType:SQUARE_TWO];
	    [player setPlayerName:[player2HumanName stringValue]];
	    break;
	case 1: /* Computer */
	    strategy = [computerStrategies objectAt:[[player2CompPopup selectedCell] tag]];
	    player = [[strategy alloc] initWithRules:rules andPieceType:SQUARE_TWO];
	    [player setSearchDepth:[[player2SearchDepth selectedCell] tag]];
	    break;
	case 2: /* Network */
	default:
	    player = [[NetworkPlayer alloc] initWithRules:rules andPieceType:SQUARE_TWO];
	    [player setHostname:[player2NetHostname stringValue]];
	    break;
    }
    [game setPlayer2:player];
    return self;
}


/*
 * dynamically loaded computer strategies
 */
 
- loadComputerStrategies
{
   DIR *dir;
   struct direct *dir_entry;
   const char *path;
   char *filename, *suffix;

   computerStrategies = [[List alloc] init];
   path = [[NXBundle mainBundle] directory];
   dir = opendir(path);
   if (dir) {
        while (dir_entry = readdir(dir)) {
            filename = dir_entry->d_name;
            if ((strcmp(filename,".") == 0) || (strcmp(filename,"..") == 0))
                continue;
            suffix = rindex(filename,'.');
            if (!suffix || 
	    	(strcmp(suffix,".cplayer") && strcmp(suffix,".bundle")))
                continue;
            [self loadComputer:filename];
        }
	closedir(dir);
    }
    return self;
}

- loadComputer:(const char *)name
{
    char path[MAXPATHLEN], cname[256];
    id newBundle, comp;

    sprintf(path,"%s/%s",[[NXBundle mainBundle] directory],name);
    newBundle = [[NXBundle alloc] initForDirectory:path];
    strcpy(path,name);
    *(rindex(path,'.')) = '\0';
    sprintf(cname,"%sComp",path);
    comp = [newBundle classNamed:cname];
    if ([computerStrategies indexOf:comp] != NX_NOT_IN_LIST)
        return comp; // this one has already been loaded
    [computerStrategies addObject:comp];
    return comp;
}

/*
 * the board... opening a new board file, etc.
 */

- openBoard:sender
{
    static id openPanel = nil;
    const char *theType = BOARD_EXTENSION, *filename;
    char const *fileTypes[2] = {0,0};
    static char boardDir[MAXPATHLEN] = {'\0'};
    int opr;
    char *ptr;
    
    if (!boardDir[0])
	strcpy(boardDir,[boardBundle directory]);
    if (!openPanel)
        openPanel = [OpenPanel new];
    if (theType && *theType)
	fileTypes[0] = theType;
    [NXApp setAutoupdate:NO];
    
    opr = [openPanel runModalForDirectory:boardDir 
			file:NULL types:fileTypes];
    if (opr) {
	filename = [openPanel filename];
	[self loadBoard:filename];
	strcpy(boardDir,filename);
	ptr = rindex(boardDir,'/');
	if (ptr)
	    *ptr = '\0';
    } else {
	[NXApp setAutoupdate:YES];
	return self;
    }
    
    return self;
}

- loadBoard:(const char *)path
{
    id oldBoard, newBoard;

    oldBoard = [miniSplatBoard board];
    newBoard = [Board newFromFile:path];
    [miniSplatBoard setScalable:YES];
    [miniSplatBoard setBoard:newBoard];
    [oldBoard free];
    [NXApp setAutoupdate:YES];
    return self;
}

- (BOOL)appAcceptsAnotherFile:sender
{
    return YES;
}

- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
    if (!strcmp(aType,BOARD_EXTENSION))
	[self loadBoard:filename];
    else if (!strcmp(aType,COMP_EXTENSION))
	[self loadComputer:filename];
    else
	return NO;
    return YES;
}


/*
 * selecting a new game setup
 */

- newGame:sender
{
    id rules, game, board;

    [newGamePanel close];
    [newGameCancel setEnabled:YES];
    rules = [[SplatRules alloc] init];
    game = [[SplatGame alloc] init];
    [game setRules:rules];
    [game setGameState:NO_GAME];
    board = [[miniSplatBoard board] trueCopy];
    [game setBoard:board];
    [board free];
    [self initializePlayersForGame:game];
    if (![game initNetPlayers]) {
	[game free];
	return self;
    }
    //? must do this to prevent deadlock
    if (![[game player1] isKindOf:[NetworkPlayer class]])
	[game showGame];

    currentGame = game;
    return self;
}

- cancelNewGame:sender
{
    [newGamePanel close];
    return self;
}

- setPlayer1Type:sender
{
    id menu, newView;
    NXRect f;
    int tag;
    
    menu = [sender selectedCell];
    tag = [menu tag];
    switch (tag) {
    	case 0: /* Human */
	    newView = player1NewHuman;
	    break;
	case 1: /* Computer */
	    newView = player1NewComputer;
	    break;
	case 2: /* Network */
	    newView = player1NewNetwork;
	    break;
	default: /* error */
	    fprintf (stderr,"Splat: unknown player type selected: %d\n",tag);
	    return self;
	    break;
    }
	
    [[player1CurrentView superview] replaceSubview:player1CurrentView
				    with:newView];
    player1CurrentView = newView;
    [player1NewGame getFrame:&f];
    [player1CurrentView setFrame:&f];
    [newView display];
    return self;
}

- setPlayer2Type:sender
{
    id menu, newView;
    NXRect f;
    int tag;
    
    menu = [sender selectedCell];
    tag = [menu tag];
    switch (tag) {
    	case 0: /* Human */
	    newView = player2NewHuman;
	    break;
	case 1: /* Computer */
	    newView = player2NewComputer;
	    break;
	case 2: /* Network */
	    newView = player2NewNetwork;
	    break;
	default: /* error */
	    fprintf (stderr,"Splat: unknown player type selected: %d\n",tag);
	    return self;
	    break;
    }
	
    [[player2CurrentView superview] replaceSubview:player2CurrentView
				    with:newView];
    player2CurrentView = newView;
    [player2NewGame getFrame:&f];
    [player2CurrentView setFrame:&f];
    [newView display];
    return self;
}

//
// control the current game
//

- pauseGame:sender
{
    [currentGame pauseGame:sender];
    return self;
}

- stopGame:sender
{
    [currentGame stopGame:sender];
    return self;
}

- startGame:sender
{
    [currentGame stopGame:sender];
    [currentGame startGame:sender];
    return self;
}


//
// delegate methods
//

- appWillTerminate:sender
{
    [defaultsManager writeDefaults];
    return self;
}

@end
