/* indent:4  tabsize:8  font:fixed-width */



#if !defined (WIN32)
#import <SoundKit/SoundKit.h>
#import <libc.h>
#endif

#import "Solitaire.h"
#import "ModuleList.h"
#import "localstrings.h"
#import <Solitaire/GameModule.h>
#import "Preferences.h"

#if defined (WIN32)
#define random() rand()
#define srandom(x) srand(x)
#endif

/* NOTE:  This class is split into categories; see also SolitaireMore.m */


@implementation Solitaire

/*---------------------------------------------------------------------------
|
|    - applicationDidFinishLaunching:notification
|
|----------------------------------------------------------------------------
|
|    Application delegate message.
|			
\---------------------------------------------------------------------------*/

- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
    NSString *autoLaunch;
    NSNotificationCenter* notificationCenter;

//    setSolEngine(self);
    
    // get set to install the inspector
    inspectorFrame = [commonInspector frame];
    currentInspector = nil;

    // make arrow keys work in game selection browser
    [gameSelectionBrowser setAcceptsArrowKeys:YES], 
	[gameSelectionBrowser setSendsActionOnArrowKeys:YES];
    [settingsPanel makeFirstResponder:gameSelectionBrowser];
    
    // go find game modules, then select and load the default
    [self getGameType];
    [self setGameIndex];

    [settingsPanel setFrameAutosaveName:@"SettingsPanel"];

    // hey, *somebody* might want to autolaunch Solitaire
    autoLaunch = [[NSUserDefaults standardUserDefaults] 
	objectForKey:@"NXAutoLaunch"];
    if (autoLaunch != nil &&
                [autoLaunch isEqualToString:@"YES"]) {
        [NSApp hide:self];
    } else {
        [settingsPanel makeKeyAndOrderFront:self];
        windowHasBeenDisplayed = YES;
    }

    // We want to clean up if the power if going to be turned off.
    notificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
    [notificationCenter addObserver:self selector:@selector (workspaceWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:[NSWorkspace sharedWorkspace]];

    srandom(time(0));
}

/*---------------------------------------------------------------------------
|
|    - applicationDidUnhide:
|
|----------------------------------------------------------------------------
|
|    Application delegate message.  Display settings panel if program is
|    being unhidden for the first time (normally after an autolaunch).
|			
\---------------------------------------------------------------------------*/

- (void) applicationDidUnhide:(NSNotification *)notification
{
    if (!windowHasBeenDisplayed)
    {
        [settingsPanel makeKeyAndOrderFront:self];
        windowHasBeenDisplayed = YES;
    }
}


/*---------------------------------------------------------------------------
|
|    - (int)app:sender openFile:(NSString *)filename 
|
|    returns:  (BOOL)  YES if file was opened
|                      NO if not
|
|----------------------------------------------------------------------------
|
|    Application delegate message requesting that a file be opened.
|			
\---------------------------------------------------------------------------*/

- (int)application:sender openFile:(NSString *)filename
{
//    setSolEngine(self);

    if ([filename pathExtension] && 
	[[filename pathExtension] isEqualToString:@"solitaire"])
    {
        NSString* fname = [filename lastPathComponent];
        NSString* dirname = @"";

        [self setGameType:fname dir:dirname];
        return YES;
    }
    else
    {
        NSBeep();
        NSRunAlertPanel(LOCALIZED_OPEN_TITLE, LOCALIZED_OPEN_ERROR, @"",
                        nil, nil, filename);
        return NO;
    }
    
}

/*--------------------------------------------------------------------------
|
|	- applicationShouldTerminate:sender
|
|	returns: YES (it should terminate always)
|
|---------------------------------------------------------------------------
|
|	This is an Application delegate message sent just before the app 
|	exits.
|	
\--------------------------------------------------------------------------*/

- (BOOL) applicationShouldTerminate:(id)sender
{
    /* kill the active game */
    [[self gameController] endGame:self];
    return YES;
}


/*--------------------------------------------------------------------------
|
|	- (void) workspaceWillPowerOff:
|
|---------------------------------------------------------------------------
|
|	This is an NSWorkspace notification that gets sent when the user
|	wants to turn off the power.
|	
\--------------------------------------------------------------------------*/

- (void) workspaceWillPowerOff:(NSNotification*)notification
{
    [[self gameController] endGame:self];
}


/*---------------------------------------------------------------------------
|
|    - selectGameIndex:
|
|----------------------------------------------------------------------------
|
|    Target of game selection browser.
|			
\---------------------------------------------------------------------------*/

- (void)selectGameIndex:sender
{
    int index = [[gameSelectionBrowser matrixInColumn:0] selectedRow];

    /* nothing to do if already selected */
    if (index == realGameIndex) return;

    /* kill the active game */
    [[self gameController] endGame:self];
    
    realGameIndex = index;
    [self setGameIndex]; 
}


/*---------------------------------------------------------------------------
|
|    - setGameIndex
|
|----------------------------------------------------------------------------
|
|    Load the chosen game.
|	     	
\---------------------------------------------------------------------------*/

- (void) setGameIndex
{
    GameModule* myController;
    NSView* newInspector;

    // save selected game to defaults
    [[NSUserDefaults standardUserDefaults] 
	setObject:[moduleList realNameAtIndex:realGameIndex] forKey:@"gameType"]; 

    // get the game controller (dynamically loads first time)
    if (!(myController = [self gameController]))
    {
        [self installInspector:nullInspector ownedBy:nil];
        return;
    }
   
    // now plug in the inspector
    newInspector = [myController inspector:self];
    if (!newInspector) newInspector = commonInspector;
    
    [self installInspector:newInspector ownedBy:myController];
    
    // start a new game
    [myController startGame:self]; 
}


/*---------------------------------------------------------------------------
|
|    - installInspector:newInspector ownedBy:myController
|
|----------------------------------------------------------------------------
|
|    Display the inspector.
|			
\---------------------------------------------------------------------------*/

- (void)installInspector:(NSView*)newInspector ownedBy:(GameModule*)myController
{
    if (newInspector != currentInspector)
    {
        [oldInspectorOwner inspectorWillBeRemoved];
        oldInspectorOwner = myController;
        
        // don't want it to resize the box.  Suboptimal technique...
        [newInspector setFrame:inspectorFrame];

        // We don't want the old inspector to be freed and removing it
        [currentInspector retain];
	
        [invisibleInspectorBox setContentView:newInspector];
        currentInspector = newInspector;

        [myController inspectorInstalled];

        [invisibleInspectorBox display];
    } 
}


/*---------------------------------------------------------------------------
|
|    - gameController
|
|    returns:  (GameeModule*) current game controller
|
|----------------------------------------------------------------------------
|
|    Return the game controller for the selected game.  Dynamically load it
|    if necessary.
|			
\---------------------------------------------------------------------------*/

- (GameModule*) gameController
{
    GameModule* theController;
    
    if (![moduleList controllerAtIndex:realGameIndex])
    {
        NSBundle *bundle;
        NSString *name;
        ModuleInfo *mp;
        NSZone *theZone;
	
        mp = [moduleList objectAtIndex:realGameIndex];
	
	// don't try to load if it failed on a previous attempt
	if ([mp failed])
	{
	    return nil;
	}
	
    name = [mp realName];
	bundle = [mp bundle];
	
	// each module gets its own zone
	theZone = NSCreateZone(NSPageSize(), NSPageSize(), YES);
	NSSetZoneName(theZone, name);

    theController = [[[bundle classNamed:name] allocWithZone:theZone]
        initFromBundle:bundle withName:name];
	if (!theController)
	{
	    [mp setFailed:YES];
	    NSRecycleZone(theZone);
            NSRunAlertPanel([[NSProcessInfo processInfo] processName], 
		LOCALIZED_LOAD_ERROR, @"", nil, nil, name);
	    return nil;
	}
	
	[mp setController:theController];
    }

    return [moduleList controllerAtIndex:realGameIndex];
}


/*---------------------------------------------------------------------------
|
|    - (BOOL)browser:sender columnIsValid:(int)column
|
|    returns:  (BOOL)	YES if browser contents are valid
|			NO if browser contents are not valid
|
|----------------------------------------------------------------------------
|
|    Delegate message of game selection browser.
|			
\---------------------------------------------------------------------------*/

- (BOOL)browser:(NSBrowser *)sender isColumnValid:(int)column
{
    return browserValid;
}


/*---------------------------------------------------------------------------
|
|    - addCellWithString:(NSString *)str at:(int)row toMatrix:matrix
|
|----------------------------------------------------------------------------
|
|    Description here.
|			
\---------------------------------------------------------------------------*/

- (void)addCellWithString:(NSString *)str at:(int)row toMatrix:matrix
{
    NSBrowserCell* theCell;
    
    [matrix insertRow:row];
    theCell = [matrix cellAtRow:row column:0];
    [theCell setStringValue:str];
    [theCell setLoaded:YES];
    [theCell setLeaf:YES]; 
}


/*---------------------------------------------------------------------------
|
|    - browser:sender fillMatrix:matrix inColumn:(int)column
|
|----------------------------------------------------------------------------
|
|    Delegate message from game selection browser.  Loads the browser.
|			
\---------------------------------------------------------------------------*/

- (void)browser:(NSBrowser *)sender createRowsForColumn:(int)column 
	inMatrix:(NSMatrix *)matrix
{
    NSString *ptr;
    int i;
    
    // this shouldn't happen...
    if (browserValid) return;

    for (i = 0; i < [moduleList count]; i++)
    {
        [self addCellWithString:[moduleList nameAtIndex:i] at:i toMatrix:matrix];
    }
           
    realGameIndex = 0;

    // select the active game
    ptr = [[NSUserDefaults standardUserDefaults] objectForKey:@"gameType"];
    if (!ptr) ptr = @"Klondike";
    if (ptr)
    {
        for (i = 0; i < [moduleList count]; i++)
        if ([ptr isEqualToString:[moduleList realNameAtIndex:i]])
        {
            realGameIndex = i;
            break;
        }
    }
    
    browserValid = YES;
}


/*----------------------------------------------------------------------------
|
|    - newGame:sender
|
|-----------------------------------------------------------------------------
|
|    Shuffle and deal the selected game.
|			
\----------------------------------------------------------------------------*/

- (void) newGame:sender
{
    [[self gameController] startGame:self]; 
}


/*----------------------------------------------------------------------------
|
|    - restartGame:sender
|
|-----------------------------------------------------------------------------
|
|    Restart the current game.
|			
\----------------------------------------------------------------------------*/

- (void) restartGame:sender
{
    [[self gameController] restartGame:self]; 
}


/*----------------------------------------------------------------------------
|
|    - showRules:sender
|
|-----------------------------------------------------------------------------
|
|    Show rules for the current game.
|			
\----------------------------------------------------------------------------*/

- (void) showRules:sender
{
    [[self gameController] showRules:self]; 
}


/*----------------------------------------------------------------------------
|
|    - showInfoPanel:
|
|-----------------------------------------------------------------------------
|
|    Display info panel.
|			
\----------------------------------------------------------------------------*/

- (void) showInfoPanel:sender
{
    if (!infoPanel)
    {
/*
    NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"Info" ofType:@"nib"];
    [NSBundle loadNibFile:path externalNameTable:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSOwner", nil] withZone:[self zone]];
*/
        [NSBundle loadNibNamed:@"Info.nib" owner:self];

	[infoPanel setFrameAutosaveName:@"InfoPanel"];
    }
    [infoPanel makeKeyAndOrderFront:nil]; 
}


/*----------------------------------------------------------------------------
|
|    - windowShouldClose:sender
|
|    returns:  (BOOL) YES to close, NO otherwise
|
|-----------------------------------------------------------------------------
|
|    Window delegate message.
|			
\----------------------------------------------------------------------------*/

- (BOOL) windowShouldClose:(id)sender
{
    if (sender == infoPanel)
    {
	infoPanel = nil;
	[sender setDelegate:nil];
    }
    else if (sender == winPanel)
    {
	winPanel = nil;
	[sender setDelegate:nil];
    }
    return YES;
}



/*----------------------------------------------------------------------------
|
|    - (CardSize)cardSize
|
|    returns:  (CardSize) current card size
|
|-----------------------------------------------------------------------------
|
|    Request for card size.
|			
\----------------------------------------------------------------------------*/

- (CardSize) cardSize
{
    return [preferencesObject cardSize];
}


/*----------------------------------------------------------------------------
|
|    - (CardBack)cardBack
|
|    returns:  (CardBack) current card back
|
|-----------------------------------------------------------------------------
|
|    Request for card back.
|			
\----------------------------------------------------------------------------*/

- (CardBack) cardBack
{
    return [preferencesObject cardBack];
}


/*----------------------------------------------------------------------------
|
|    - (NSColor*) backgroundColor
|
|    returns:  (NSColor*) current background color
|
|-----------------------------------------------------------------------------
|
|    Request for background color.
|			
\----------------------------------------------------------------------------*/

- (NSColor *) backgroundColor
{
    return [preferencesObject backgroundColor];
}


/*----------------------------------------------------------------------------
|
|    - imageForSize:(CardSize)aSize
|
|    returns:  (NSImage *) custom card back
|
|-----------------------------------------------------------------------------
|
|    Request for card back custom image.
|			
\----------------------------------------------------------------------------*/

- (NSImage*) imageForSize:(CardSize)aSize
{
    return [preferencesObject imageForSize:aSize];
}


/*----------------------------------------------------------------------------
|
|    - win
|
|-----------------------------------------------------------------------------
|
|    Default win routine.
|			
\----------------------------------------------------------------------------*/

- (void)win
{
    if (!winPanel)
    {
/*
    NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"Winner.nib" ofType:@"nib"];
    [NSBundle loadNibFile:path externalNameTable:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSOwner", nil] withZone:[self zone]];
    };
*/
        [NSBundle loadNibNamed:@"Winner.nib" owner:self];
    }
    [winPanel makeKeyAndOrderFront:self];
#if !defined (WIN32)    
    [[Sound findSoundFor:@"Trumpet.snd"] play];
#endif
}

@end
