//
//  This file is a part of Empire, a game of exploration and conquest.
//  Copyright (C) 1996  Steve Nygard
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  You may contact the author by:
//     e-mail:  nygard@telusplanet.net
//

#import "Empire.h"

RCSID ("$Id: Brain.m,v 1.17 1997/10/31 04:51:32 nygard Exp $");

#import "Brain.h"
#import "DistributedGameManager.h"
#import "Map.h"
#import "MapView.h"
#import "EmPlayer.h"
#import "ClientController.h"
#import "ServerController.h"
#import "EditorController.h"
#import "NewGameController.h"
#import "EmInspectorManager.h"

#define LIBRARY_SUBPATH @"Empire"
#define MODULE_EXTENSION @"eplayer"

@interface NSConnection (PrivateMethods)
+ (void) _enableLogging:(char)fp16;
+ (void) _toggleLogging;
@end

//======================================================================
// The Brain is the application delegate, and does things that
// don't fit anywhere else.
//======================================================================

static Brain *_instance = nil;

#define Brain_VERSION 1

@implementation Brain

+ (void) initialize
{
    NSUserDefaults *defaults;
    NSMutableDictionary *empireDefaults;

    if (self == [Brain class])
    {
        [self setVersion:Brain_VERSION];

        defaults = [NSUserDefaults standardUserDefaults];
        empireDefaults = [NSMutableDictionary dictionary];

        [empireDefaults setObject:@"NO"      forKey:DK_DMakeActive];
        [empireDefaults setObject:@"default" forKey:DK_DefaultMap];
        [empireDefaults setObject:@"None"    forKey:DK_DefaultPlayer1Type];
        [empireDefaults setObject:@"None"    forKey:DK_DefaultPlayer2Type];
        [empireDefaults setObject:@"None"    forKey:DK_DefaultPlayer3Type];
        [empireDefaults setObject:@"Larry"   forKey:DK_DefaultPlayer1Name];
        [empireDefaults setObject:@"Curly"   forKey:DK_DefaultPlayer2Name];
        [empireDefaults setObject:@"Moe"     forKey:DK_DefaultPlayer3Name];
        [empireDefaults setObject:@"80"      forKey:DK_MapWidth];
        [empireDefaults setObject:@"50"      forKey:DK_MapHeight];
        [empireDefaults setObject:@"0.5"     forKey:DK_CombatEventDelay];
        [empireDefaults setObject:@"NO"      forKey:DK_DebugCombatEvents];
        [empireDefaults setObject:@"YES"     forKey:DK_SeedRandom];
        [defaults registerDefaults:empireDefaults];
    }
}

//----------------------------------------------------------------------

+ (Brain *) instance
{
    if (_instance == nil)
        _instance = [[Brain alloc] init];

    return _instance;
}

//----------------------------------------------------------------------

- init
{
    // This is somewhat of a hack.
    NSAssert (_instance == nil, @"Allocated more than one brain.");
    _instance = self;

    empirePlayerBundles = [[NSMutableArray array] retain];

    return self;
}

//----------------------------------------------------------------------

- (void) dealloc
{
    SNRelease (empirePlayerBundles);
    SNRelease (clientController);
    SNRelease (serverController);
    SNRelease (editorController);
    SNRelease (newGameController);

    [super dealloc];
}

//======================================================================
// Interface Managment
//======================================================================

- (void) showInfoPanel:sender
{
    if (infoPanel == nil)
    {
        BOOL loaded = [NSBundle loadNibNamed:@"InfoPanel.nib" owner:self];

        NSAssert (loaded == YES, @"Could not load InfoPanel.nib");

        [versionTextfield setStringValue:EMPIRE_VERSION];
    }

    [infoPanel makeKeyAndOrderFront:nil];
}

//----------------------------------------------------------------------

- (void) newGame:sender
{
    if (newGameController == nil)
        newGameController = [[NewGameController alloc] initWithBrain:self];

    [newGameController showPanel];
}

//----------------------------------------------------------------------

- (void) startServer:sender
{
    if (serverController == nil)
        serverController = [[ServerController alloc] init];

    [serverController showPanel];
}

//----------------------------------------------------------------------

- (void) startClient:sender
{
    if (clientController == nil)
        clientController = [[ClientController alloc] initWithBrain:self];

    [clientController showPanel];
}

//----------------------------------------------------------------------

- (void) newMap:sender
{
    if (editorController == nil)
        editorController = [[EditorController alloc] init];

    [editorController newMap];
}

//----------------------------------------------------------------------

- (void) open:sender
{
    if (editorController == nil)
        editorController = [[EditorController alloc] init];

    [editorController open:sender];
}

//----------------------------------------------------------------------

- (NewGameController *) newGameController
{
    if (newGameController == nil)
        newGameController = [[NewGameController alloc] initWithBrain:self];

    return newGameController;
}

//----------------------------------------------------------------------

- (ServerController *) serverController
{
    if (serverController == nil)
        serverController = [[ServerController alloc] init];

    return serverController;
}

//----------------------------------------------------------------------

- (BOOL) validateMenuItem:(NSMenuItem *)menuCell
{
    SEL action = [menuCell action];
    BOOL valid = NO;

    if (action == @selector (open:)
        || action == @selector (newMap:)
        || action == @selector (newGame:)
        || action == @selector (startServer:)
        || action == @selector (startClient:)
        || action == @selector (showInfoPanel:))
    {
        valid = YES;
    }
    else if (action == @selector (toggleBerserk:)
             || action == @selector (showCrystalBall:)
             || action == @selector (stopGame:))
    {
        id responder = [NSApp targetForAction:@selector (gameManager)];
        GameManager *gameManager;

        if (responder != nil)
        {
            gameManager = [responder gameManager];
            valid = [gameManager validateMenuItem:menuCell];
        }

        //NSLog (@"validating: %@, valid: %@", NSStringFromSelector (action), valid == YES ? @"Yes" : @"No");
        //NSLog (@"Responder was: %@", responder);
    }

    //NSLog (@"validating: %@, valid: %@", NSStringFromSelector (action), valid == YES ? @"Yes" : @"No");

    return valid;
}

//----------------------------------------------------------------------
// The player bundle code comes from Risk...
//----------------------------------------------------------------------

- (void) loadEmpirePlayerBundles
{
    NSBundle *mainBundle;
    NSMutableSet *loadedEmpirePlayerNames;
    NSMutableSet *delayedEmpirePlayerPaths;
    NSMutableSet *tempPlayerNames;
    NSArray *resourcePaths;
    NSEnumerator *pathEnumerator;
    NSBundle *playerBundle;
    BOOL keepTrying;
    NSMutableDictionary *loadedBundles;

    NSString *path;

    mainBundle = [NSBundle mainBundle];
    loadedEmpirePlayerNames = [NSMutableSet set];
    delayedEmpirePlayerPaths = [NSMutableSet set];
    tempPlayerNames = [NSMutableSet set];
    loadedBundles = [NSMutableDictionary dictionary];

    resourcePaths = [mainBundle pathsForResourcesOfType:@"empireplayer" inDirectory:nil];
    //NSLog (@"resource paths: %@", resourcePaths);

    pathEnumerator = [resourcePaths objectEnumerator];
    while (path = [pathEnumerator nextObject])
    {
        NSString *str = [[path lastPathComponent] stringByDeletingPathExtension];

        // refuse to load if the name matches a module already loaded
        if ([loadedEmpirePlayerNames containsObject:str] == NO)
        {
            // OK, all is well -- go load the little bugger
            //NSLog (@"Load empire player bundle %@", path);
            playerBundle = [NSBundle bundleWithPath:path];
            if ([playerBundle principalClass] == nil)
            {
                // Ugh, failed.  Put the class name in tempStorage in case
                // it can't be loaded because it's a subclass of another
                // CP who hasn't been loaded yet.
                [delayedEmpirePlayerPaths addObject:path];
            }
            else
            {
                // it loaded so add it to the list.
                [loadedBundles setObject:playerBundle forKey:str];
                //NSLog (@"str: %@, playerBundle: %@", str, playerBundle);
                //NSLog (@"priciple class is %@", [playerBundle principalClass]);
                [loadedEmpirePlayerNames addObject:str];
            }
        }
    }

    // now loop and keep trying to load the ones in tempStorage.  Keep trying
    // as long as at least one of the failed ones succeeds each time through
    // the loop

    do
    {
        keepTrying = NO;
        pathEnumerator = [delayedEmpirePlayerPaths objectEnumerator];
        while (path = [pathEnumerator nextObject])
        {
            playerBundle = [NSBundle bundleWithPath:path];
            if ([playerBundle principalClass] != nil)
            {
                NSString *str = [[path lastPathComponent] stringByDeletingPathExtension];

                [loadedBundles setObject:playerBundle forKey:str];
                //NSLog (@"str: %@, playerBundle: %@", str, playerBundle);
                //NSLog (@"(delayed) priciple class is %@", [playerBundle principalClass]);
                keepTrying = YES;
                [tempPlayerNames addObject:path];
                [loadedEmpirePlayerNames addObject:str];
            }
        }

        [delayedEmpirePlayerPaths minusSet:tempPlayerNames];
        [tempPlayerNames removeAllObjects];
    }
    while (keepTrying == YES);

    // now cpNameStorage contains a list of all the menu strings.
    // we must add them to the menus in the panel.

    //NSLog (@"info: %@", [testBundle infoDictionary]);

    [empirePlayerBundles addObjectsFromArray:[loadedBundles allValues]];
}

//----------------------------------------------------------------------

- (NSArray *) empirePlayerBundles
{
    return empirePlayerBundles;
}

//----------------------------------------------------------------------

// Just testing this...
- (void) bundleLoaded:(NSNotification *)aNotification
{
    NSDictionary *userInfo = [aNotification userInfo];
    NSArray *loadedClasses = [userInfo objectForKey:@"NSLoadedClasses"];
    NSEnumerator *enumerator = [loadedClasses objectEnumerator];
    id object;

    while (object = [enumerator nextObject])
    {
        //NSLog (@"Loaded class '%@'\n", NSStringFromClass ([object class]));
    }
}


//======================================================================
// Application Delegate
//======================================================================

- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    EmInspectorManager *inspectorManager;
    BOOL flag;

    // Cause the Inspector Manager to load its panel.
    inspectorManager = [EmInspectorManager instance];
    [inspectorManager player:p_neutral selectThing:nil];
    [inspectorManager showPanel];

    [self loadEmpirePlayerBundles];

    flag = [defaults boolForKey:DK_DMakeActive];

    if (flag == YES)
    {
        [NSApp activateIgnoringOtherApps:YES];
    }

    [defaultCenter addObserver:self
                   selector:@selector (bundleLoaded:)
                   name:NSBundleDidLoadNotification
                   object:NSApp];

}

//======================================================================
// GameManager first responder chaining
//======================================================================

//----------------------------------------------------------------------
// We need to do this so that these commands are available while the
// player windows are key.
//----------------------------------------------------------------------

- (void) toggleBerserk:sender
{
    id responder = [NSApp targetForAction:@selector (gameManager)];
    GameManager *gameManager;

    if (responder != nil)
    {
        gameManager = [responder gameManager];
        [gameManager toggleBerserk:sender];
    }
}

//----------------------------------------------------------------------

- (void) showCrystalBall:sender
{
    id responder = [NSApp targetForAction:@selector (gameManager)];
    GameManager *gameManager;

    if (responder != nil)
    {
        gameManager = [responder gameManager];
        [gameManager showCrystalBall:sender];
    }
}

//----------------------------------------------------------------------

- (void) stopGame:sender
{
    id responder = [NSApp targetForAction:@selector (gameManager)];
    GameManager *gameManager;

    if (responder != nil)
    {
        gameManager = [responder gameManager];
        [gameManager stopGame];
    }
}

@end
