//
// $Id: Human.m,v 1.3 1997/10/31 03:38:30 nygard Exp $
//

//
//  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: Human.m,v 1.3 1997/10/31 03:38:30 nygard Exp $");

#import "Human.h"

#import "Brain.h"
#import "City.h"
#import "CombatEvent.h"
#import "DistributedGameManager.h"
#import "EmInspectorManager.h"
#import "Map.h"
#import "MapPathView.h"
#import "Unit.h"
#import "WorldMapController.h"

#import "HState.h"
#import "HCombatReportState.h"
#import "HDirectionState.h"
#import "HEscortShipState.h"
#import "HFlightPathState.h"
#import "HIdleState.h"
#import "HInitialState.h"
#import "HMoveState.h"
#import "HMoveToState.h"
#import "HPatrolToState.h"
#import "HProductionState.h"
#import "HSetFlightPathState.h"
#import "HSurveyState.h"
#import "HGameOverState.h"

//======================================================================

@implementation Human

- (void) awakeFromNib
{
    NSString *imagePath;
    NSImage *image;

    imagePath = [[NSBundle mainBundle] pathForImageResource:@"mwi_map.tiff"];
    NSAssert (imagePath != nil, @"Couldn't find mwi_map.tiff");

    image = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];
    NSAssert (image != nil, @"Couldn't load mwi_map.tiff");

    [playerWindow setMiniwindowImage:image];

    [self retain]; // Released when window closed
}

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

- initWithName:(NSString *)aName number:(Player)number world:(Map *)aMap
       capital:(City *)capitalCity withEfficiencies:(int)pe:(int)ce
   gameManager:(GameManager *)theGameManager
{
    EMMapLocation cityLocation;
    NSRect frameRect;
    BOOL loaded;
  
    [super initWithName:aName
           number:number
           world:aMap
           capital:capitalCity
           withEfficiencies:pe:ce
           gameManager:theGameManager];

    loaded = [NSBundle loadNibNamed:@"Human.nib" owner:self];
    NSAssert (loaded == YES, @"Could not load Human.nib");
    
    frameRect = [playerWindow frame];
    frameRect.origin.x += number * 24;
    frameRect.origin.y -= number * 24;
    [playerWindow setFrameOrigin:frameRect.origin];

    [mapView setMap:map];
    cityLocation = [capitalCity cityLocation];
    [mapView positionCursorAtLocation:cityLocation];
    [mapView centerScreenAroundCursor];
    [playerWindow makeFirstResponder:mapView];
    [playerWindow setTitle:[NSString stringWithFormat:@"Player %d - %@", number, aName]];

    [mapView useDefaultCursor];
    [mapView setCursorEnabled:YES];

    [turnNumberTextfield setIntValue:0];

    //[playerWindow setFrame:frameRect display:YES];
    [playerWindow orderFront:nil];

    state = nil;
    savedStateKey = nil;

    selectedUnit = nil;

    worldMapController = nil;
    selectedCity = nil;

    combatEvents = [[NSMutableArray array] retain];
    savedMap = nil;

    stateGroup = [[NSMutableDictionary dictionary] retain];
    [stateGroup setObject:[[[HCombatReportState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_COMBAT_REPORT];
    [stateGroup setObject:[[[HDirectionState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_DIRECTION];
    [stateGroup setObject:[[[HEscortShipState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_ESCORT_SHIP];
    [stateGroup setObject:[[[HFlightPathState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_FLIGHT_PATH];
    [stateGroup setObject:[[[HIdleState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_IDLE];
    [stateGroup setObject:[[[HInitialState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_INITIAL];
    [stateGroup setObject:[[[HMoveState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_MOVE];
    [stateGroup setObject:[[[HMoveToState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_MOVE_TO];
    [stateGroup setObject:[[[HPatrolToState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_PATROL_TO];
    [stateGroup setObject:[[[HProductionState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_PRODUCTION];
    [stateGroup setObject:[[[HSetFlightPathState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_SET_FLIGHT_PATH];
    [stateGroup setObject:[[[HSurveyState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_SURVEY];
    [stateGroup setObject:[[[HGameOverState alloc] initWithGameManager:gameManager] autorelease] forKey:HS_GAME_OVER];

    [self changeState:HS_IDLE];

    return self;
}

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

- (void) dealloc
{
    SNRelease (worldMapController);

    SNRelease (savedStateKey);
    SNRelease (stateGroup);
    SNRelease (combatEvents);
    SNRelease (savedMap);

    [super dealloc];
}

//======================================================================
// Turn phases
//======================================================================

- (void) yourTurn:(int)turn
{
    [map detach:self];

    [super yourTurn:turn];

    selectedUnit = nil;

    [turnNumberTextfield setIntValue:turn];

    if ([playerWindow isVisible] == YES)
        [playerWindow makeKeyAndOrderFront:self];

    if (state != nil)
        [state beginTurn:self];
}

//======================================================================
// Interface Management
//======================================================================

- (void) continue:sender
{
    if (state != nil)
        [state continue:self];
}

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

- (void) endTurn:sender
{
    if (state != nil)
        [state endTurn:self];

    [combatEvents removeAllObjects];

    SNRelease (savedMap);

    savedMap = [map copyWithZone:[self zone]];

    [map attach:self];
}

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

- (void) enableContinueButton:(BOOL)flag
{
    if (flag == NO && [playerWindow firstResponder] == continueButton)
        [playerWindow makeFirstResponder:endTurnButton];
    
    [continueButton setEnabled:flag];
}

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

- (void) enableEndTurnButton:(BOOL)flag
{
    [endTurnButton setEnabled:flag];

    if (flag == NO && [playerWindow firstResponder] == endTurnButton)
        [playerWindow makeFirstResponder:mapView];
    //[playerWindow makeFirstResponder:continueButton];
}

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

// Hmm. Always default to NO, and if super returns NO, run through our checks?

- (BOOL) validateMenuItem:(NSMenuItem *)menuCell
{
    SEL action = [menuCell action];
    BOOL valid = NO;
    
    if (action == @selector (showCrystalBall:)
        || action == @selector (toggleBerserk:)
        || action == @selector (stopGame:))
    {
        valid = [super validateMenuItem:menuCell];
    }
    else if (action == @selector (showWarReport:))
    {
        valid = gameOver == NO;
    }
    else if (action == @selector (showWorldMap:)
        || action == @selector (showShipReport:))
    {
        valid = YES;
    }
    else if (action == @selector (resignFromGame:))
    {
        valid = YES;
    }
    else if (state != nil)
    {
        valid = [state validateMenuItem:self:menuCell];
    }

    return valid;
}

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

- (void) clickActiveButton
{
    if ([endTurnButton isEnabled] == YES)
        [endTurnButton performClick:nil];
    else if ([continueButton isEnabled] == YES)
        [continueButton performClick:nil];
}

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

- (void) setStatusLine:(NSString *)text
{
    if (text != nil)
        [statusTextfield setStringValue:text];
}

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

- (void) selectThing:object
{
    [[EmInspectorManager instance] player:playerNumber selectThing:object];
}

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

- (void) selectUnit:(Unit *)aUnit
{
    selectedUnit = aUnit;
}

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

- (MapView *) mapView
{
    return mapView;
}

//======================================================================-
// Commands
//======================================================================-

- (void) moveMode:sender
{
    if (state != nil)
        [state moveMode:self];
}

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

- (void) surveyMode:sender
{
    if (state != nil)
        [state surveyMode:self];
}

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

- (void) groupMode:sender
{
    if (state != nil)
        [state groupMode:self];
}

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

- (void) wait:sender
{
    if (state != nil && selectedUnit != nil)
        [state wait:self unit:selectedUnit];
}

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

- (void) flightPaths:sender
{
    if (state != nil)
        [state flightPathMode:self];
}

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

- (void) activateUnit:sender
{
    if (state != nil && selectedUnit != nil)
        [state activate:self unit:selectedUnit];
}

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

- (void) centerScreen:sender
{
    if (state != nil)
        [state centerScreen:self mapView:mapView];
}

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

- (void) centerCursor:sender
{
    if (state != nil)
        [state centerCursor:self mapView:mapView];
}

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

- (void) centerSelected:sender
{
    if (state != nil)
        [state centerSelected:self mapView:mapView];
}

//======================================================================
// End of game Reports
//======================================================================

- (void) showFinalCombinedMap:sender
{
    if (state != nil)
        [state showFinalMap:self forPlayer:p_neutral];
}

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

- (void) showFinalPlayer1Map:sender
{
    if (state != nil)
        [state showFinalMap:self forPlayer:p_player1];
}

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

- (void) showFinalPlayer2Map:sender
{
    if (state != nil)
        [state showFinalMap:self forPlayer:p_player2];
}

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

- (void) showFinalPlayer3Map:sender
{
    if (state != nil)
        [state showFinalMap:self forPlayer:p_player3];
}

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

- (void) showFinalMapForPlayer:(Player)number
{
    if (finalMaps[number] == nil)
        return;

    if (number == p_neutral)
        [statusTextfield setStringValue:@"Final Combined Map"];
    else
        [statusTextfield setStringValue:[NSString stringWithFormat:@"Final Player %d Map", number]];

    // main and world maps...

    [mapView setMap:finalMaps[number]];
    if (worldMapController != nil)
    {
        [worldMapController setMap:finalMaps[number]];
    }
}

//======================================================================-
// Orders
//======================================================================-

- (void) goDirection:sender
{
    if (state != nil)
        [state goDirection:self];
}

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

- (void) goHome:sender
{
    if (state != nil && selectedUnit != nil)
        [state goHome:self unit:selectedUnit];
}

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

- (void) goRandom:sender
{
    if (state != nil && selectedUnit != nil)
        [state goRandom:self unit:selectedUnit];
}

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

- (void) moveTo:sender
{
    if (state != nil)
        [state moveTo:self];
}

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

- (void) patrolTo:sender
{
    if (state != nil)
        [state patrolTo:self];
}

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

- (void) escortShip:sender
{
    if (state != nil)
        [state escortShip:self];
}

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

- (void) explore:sender
{
    if (state != nil && selectedUnit != nil)
        [state explore:self unit:selectedUnit];
}

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

- (void) sentry:sender
{
    if (state != nil && selectedUnit != nil)
        [state sentry:self unit:selectedUnit];
}

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

- (void) clearOrders:sender
{
    if (state != nil)
    {
        if (selectedUnit != nil)
        {
            [state activate:self unit:selectedUnit];
            //[state clearOrders:self unit:selectedUnit];
        }
        else
        {
            City *city = [self cityAtLocation:[mapView cursorLocation]];

            [state clearOrders:self city:city];
        }
    }
}

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

- (void) loadShip:sender
{
    if (state != nil && selectedUnit != nil)
        [state loadShip:self unit:selectedUnit];
}

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

- (void) unloadShip:sender
{
    if (state != nil && selectedUnit != nil)
        [state unloadShip:self unit:selectedUnit];
}

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

- (void) skipMove:sender
{
    if (state != nil && selectedUnit != nil)
        [state skipMove:self unit:selectedUnit];
}

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

- (void) resignFromGame:sender
{
    NSAssert (gameOver == NO, @"The game is already over.");

    // Confirm..
    [self resign];
}

//======================================================================
// Reports
//======================================================================

- (void) showCombatReport:sender
{
     if (state != nil)
        [state combatReport:self];
}

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

- (void) showProductionMap:sender
{
    if (state != nil)
        [state productionMap:self];
}

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

- (void) showShipReport:sender
{
    [gameManager showShipReport:sender forPlayer:self];
}

//----------------------------------------------------------------------
// We need the game manager to have a world, so that we can get the
// worldCityCount for the war report even after the game is over.
//----------------------------------------------------------------------

- (void) showWarReport:sender
{
    NSAssert (gameOver == NO, @"The game is over.");
    [gameManager showWarReport:sender forPlayer:self];
}

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

- (void) showWorldMap:sender
{
    if (worldMapController == nil)
    {
        worldMapController = [[WorldMapController alloc] init];
        [worldMapController setMap:map];
        [worldMapController setDelegate:self];

        [worldMapController setTitle:[NSString stringWithFormat:@"Player %d World Map", playerNumber] autosaveFrame:YES];
    }

    [worldMapController showPanel];
}

//======================================================================
// MapView delegate
//======================================================================

- (void) mouseDown:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    if (state != nil)
        [state mouseDown:self:modifierFlags atLocation:target];
}

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

- (void) mouseUp:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    if (state != nil)
        [state mouseUp:self:modifierFlags atLocation:target];
}

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

- (void) rightMouseDown:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    if (state != nil)
        [state rightMouseDown:self:modifierFlags atLocation:target];
}

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

- (void) rightMouseUp:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    if (state != nil)
        [state rightMouseUp:self:modifierFlags atLocation:target];
}

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

- (void) keyDown:(NSEvent *)theEvent
{
    if (state != nil)
        [state keyDown:self:theEvent];
}

//======================================================================
// Window Delegate
//======================================================================

- (BOOL) windowShouldClose:sender
{
    BOOL shouldClose = YES;
    int result;
    
    // This may also be the delegate of the world map window.
    // Only if still connected, running...
    if (sender == playerWindow && gameOver == NO)
    {
        result = NSRunAlertPanel (@"Exit Game", @"Closing this window will end the game for player %d.",
                                  @"Keep playing", @"Exit game", nil, playerNumber);
        shouldClose = result == NSAlertAlternateReturn;

        if (shouldClose == YES)
        {
            // Notify game manager...
            [self resignFromGame:nil];
        }
    }

    return shouldClose;
}

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

- (void) windowWillClose:(NSNotification *)aNotification
{
    NSWindow *window;

    window = [aNotification object];

    if (window == playerWindow)
    {
        [self autorelease]; // Important for timing.
    }
}

//======================================================================
// End of player
//======================================================================

- (void) gameStopped:(int)activePlayers
{
    [super gameStopped:activePlayers];

    [self changeState:HS_GAME_OVER];
    [self setStatusLine:@"The game has been stopped."];
}

//----------------------------------------------------------------------
// Returns YES to continue playing, NO to stop game.
// Does this block (whirling disk) the server?
//----------------------------------------------------------------------

- (BOOL) playerHasWon:(int)activePlayers
{
    BOOL keepPlaying = YES;
    int result;

    result = NSRunAlertPanel (@"Game Over", @"Player %d has won.", @"End Game", @"Continue Moving", nil, playerNumber);

    if (result == NSAlertDefaultReturn)
    {
        [super playerHasWon:activePlayers];

        [self changeState:HS_GAME_OVER];
        [self setStatusLine:@"You win."];
        keepPlaying = NO;
    }

    return keepPlaying;
}

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

- (void) playerHasLost:(int)activePlayers
{
    [super playerHasLost:activePlayers];

    [self changeState:HS_GAME_OVER];
    [self setStatusLine:@"All your cities have been captured."];
}

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

- (void) playerHasResigned:(int)activePlayers
{
    [super playerHasResigned:activePlayers];

    [self changeState:HS_GAME_OVER];
    [self setStatusLine:@"You have resigned from the game."];
}

//======================================================================
// HState Management
//======================================================================

- (void) changeState:(NSString *)newStateName
{
    HState *newState;

    newState = [stateGroup objectForKey:newStateName];
    NSAssert1 (newState != nil, @"Couldn't find state '%@' in group.", newStateName);

    if (state != nil)
        [state stateExited:self];

    state = newState;

    [modeTextfield setStringValue:[state stateName]];
    [state stateEntered:self];
}

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

- (void) saveState
{
    // We can only save one state at a time.
    NSAssert (savedStateKey == nil, @"Saved state not nil.");

    savedStateKey = [[state stateKey] retain];
}

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

- (void) restoreState
{
    NSAssert (savedStateKey != nil, @"Saved state is nil");

    [self changeState:savedStateKey];
    SNRelease (savedStateKey);
}

//======================================================================
// HState Access
//======================================================================

- (City *) selectedCity
{
    return selectedCity;
}

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

- (void) selectCity:(City *)city
{
    selectedCity = city;
}

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

- (Map *) finalMapForPlayer:(Player)number
{
    return finalMaps[number];
}

//======================================================================
// Combat Report
//======================================================================

- (Map *) savedMap
{
    return savedMap;
}

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

- (NSArray *) combatEvents
{
    return combatEvents;
}

//======================================================================
// Map Delegate
//======================================================================

//----------------------------------------------------------------------
// The methods record combat events.  They are only observed while
// other players are playing their turns.
//----------------------------------------------------------------------

- (void) refreshLocation:(EMMapLocation)target
{
    [combatEvents addObject:[CombatEvent updateAtLocation:target ofMap:map]];
}

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

- (void) refresh3x3Location:(EMMapLocation)target
{
    [combatEvents addObject:[CombatEvent update3x3AroundLocation:target ofMap:map]];
}

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

- (void) strippedIconsOfPlayer:(Player)number
{
    [combatEvents addObject:[CombatEvent stripIconsOfPlayer:number]];
}

//======================================================================
// Other
//======================================================================

- (void) showExplosions:(int)count atLocation:(EMMapLocation)target
{
    [super showExplosions:count atLocation:target];
    
    [mapView showExplosions:count atLocation:target];
}

//======================================================================
// Subclass Notifications
//======================================================================

- (void) selectedUnitTriedToMove:(MoveResult)moveResult
{
    [super selectedUnitTriedToMove:moveResult];

    if (moveResult == mr_moved || moveResult == mr_victory)
    {
        [mapView setCursorEnabled:NO];
        [mapView positionCursorAtLocation:[selectedUnit unitLocation]];
        [mapView setCursorEnabled:YES];
    }
    else if (moveResult == mr_destroyed)
    {
        [mapView setCursorEnabled:NO];
    }
    else if (moveResult == mr_blocked)
    {
        [mapView setFastCursor:YES];
    }
}

@end
