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

#import "HSurveyState.h"

#import "City.h"
#import "GameManager.h"
#import "HCombatReportState.h"
#import "HDirectionState.h"
#import "HEscortShipState.h"
#import "HFlightPathState.h"
#import "HIdleState.h"
#import "HMoveState.h"
#import "HMoveToState.h"
#import "HPatrolToState.h"
#import "HProductionState.h"
#import "Human.h"
#import "Map.h"
#import "MapView.h"
#import "Unit.h"

#import "../Orders.subproj/Orders.h"

//======================================================================
// This state gives the user another chance to change commands of
// units before the turn ends.  The user can give them orders which will
// be executed on the next turn.
//======================================================================

@implementation HSurveyState

- initWithGameManager:(GameManager *)theGameManager
{
    [super initWithGameManager:theGameManager];

    draggingFromUnit = NO;

    return self;
}

//======================================================================
// Subclass responsibilities
//======================================================================

- (NSString *) stateName
{
    return @"Survey";
}
  
//----------------------------------------------------------------------

- (NSString *) stateKey
{
    return HS_SURVEY;
}

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

- (void) stateEntered:(Human *)context
{
    MapView *mapView = [context mapView];
    EMMapLocation cursorLocation;
  
    draggingFromUnit = NO;

    [mapView enableCompressedEvents:YES];
    [mapView useDefaultCursor];
    [mapView setFastCursor:NO];
    [mapView setCursorEnabled:YES];
    [context setStatusLine:@""];

    // Disable both buttons
    [context enableContinueButton:NO];
    if ([context nextUnitAwaitingMovement] == nil) // clears selected unit..
    {
        [context enableEndTurnButton:YES];

        if ([gameManager isBerserk] == YES)
        {
            [self endTurn:context];
        }
        else
        {
            cursorLocation = [mapView cursorLocation];
            [self selectThing:context atLocation:cursorLocation andInspect:YES];
        }            
    }
    else
    {
        [context enableEndTurnButton:NO];
  
        //[context selectUnit:[context nextUnit]];

        cursorLocation = [mapView cursorLocation];
        [self selectThing:context atLocation:cursorLocation andInspect:YES];
        [context setStatusLine:[[context selectedUnit] statusLine]];
    }
}
  
//----------------------------------------------------------------------

- (void) endTurn:(Human *)context
{
    // Should this be executed when we enter the idle state?
    // I don't think so -- we "enter" the idle state upon startup, but
    // don't want to execute this code.
    [context turnDone];

    [context changeState:HS_IDLE];
}

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

- (void) combatReport:(Human *)context
{
    [context saveState];
    [context changeState:HS_COMBAT_REPORT];
}

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

// This is a bit unusual: You can select nothing, drag over a unit, and
// it will enter the MoveTo state...

- (void) mouseDown:(Human *)context:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    Map *map = [context map];
    MapToken mapToken = [map tokenAtLocation:target];
    Unit *unit;
    EMMapLocation unitLocation;
    Player player;
    Icon icon;
    BOOL explored;
  
    [[context mapView] positionCursorAtLocation:target];

    //[context selectUnit:nil];

    EMMapTokenComponents (mapToken, NULL, &player, &icon, &explored);

    if (draggingFromUnit == NO
        && player == [context playerNumber]
        && icon != i_none
        && explored == YES)
    {
        unit = [context unitWithType:EMConvertIconToUnitType (icon) atLocation:target];
        if (unit != nil)
        {
            [context selectUnit:unit];
            [context setStatusLine:[unit statusLine]];
            draggingFromUnit = YES;
        }
    }
    else
    {
        [context setStatusLine:[NSString stringWithFormat:@"Loc: %d,%d", target.row, target.column]];
        unit = [context selectedUnit];
        unitLocation = [unit unitLocation];
        if (draggingFromUnit == YES && unit != nil && (target.row != unitLocation.row || target.column != unitLocation.column))
        {
            [context saveState];
            if ((modifierFlags & NSControlKeyMask) != 0)
                [context changeState:HS_PATROL_TO];
            else
                [context changeState:HS_MOVE_TO];
        }
    }
}

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

- (void) mouseUp:(Human *)context:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    [self selectThing:context atLocation:target andInspect:YES];
    draggingFromUnit = NO;
}

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

- (void) rightMouseUp:(Human *)context:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
{
    [self moveMode:context];
}

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

- (void) keyDown:(Human *)context:(NSEvent *)theEvent
{
    NSString *eventCharacters = [theEvent characters];
    unichar uc = [eventCharacters characterAtIndex:0];
    
    // I'm going to assume that this is okay, because the alternatives
    // seem worse.

    switch (uc)
    {
      case '5':
      case 'c':
      case 'C':
          [context centerScreen:nil];
          break;

      case 'd':
      case 'D':
          [context goDirection:nil];
          break;

      case 'e':
      case 'E':
          [context escortShip:nil];
          break;

      case 'h':
      case 'H':
          [context goHome:nil];
          break;

      case 'l':
      case 'L':
          [context loadShip:nil];
          break;
      
      case 'm':
      case 'M':
          [self moveMode:context];
          break;

      case 'o':
      case 'O':
          // Can clear orders for city too.
          //[context activateUnit:nil];
          [context clearOrders:nil];
          break;

      case 'p':
      case 'P':
          [self patrolTo:context];
          break;

      case 'r':
      case 'R':
          [context goRandom:nil];
          break;

      case 'S':
      case 's':
          [context sentry:nil];
          break;

      case 't':
      case 'T':
          [context moveTo:nil];
          break;

      case 'u':
      case 'U':
          [context unloadShip:nil];
          break;
      
      case 'x':
      case 'X':
          [context explore:nil];
          break;

      case 3: // This is the Enter key on the number pad... at least, on my machine.
          [context clickActiveButton];
          break;

      default:
          [super keyDown:context:theEvent];
          break;
    }
}

//======================================================================
// Commands
//======================================================================

- (void) moveMode:(Human *)context
{
    if ([context nextUnitAwaitingMovement] != nil)
        [context changeState:HS_MOVE];
}

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

- (void) flightPathMode:(Human *)context
{
    [context saveState];
    [context changeState:HS_FLIGHT_PATH];
}

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

- (void) activate:(Human *)context unit:(Unit *)aUnit
{
    [self clearOrders:context unit:aUnit];
    [context activateThisUnit:aUnit];
    [self moveMode:context];
}

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

- (void) centerScreen:(Human *)context mapView:(MapView *)mapView
{
    [mapView centerScreenAroundCursor];
}

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

- (void) productionMap:(Human *)context
{
    [context saveState];
    [context changeState:HS_PRODUCTION];
}

//======================================================================
// Orders
//======================================================================

- (void) goDirection:(Human *)context
{
    [context saveState];
    [context changeState:HS_DIRECTION];
}

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

- (void) goHome:(Human *)context unit:(Unit *)aUnit
{
    City *target_city;
  
    // Find nearest city.
    target_city = [context ourCityNearestToLocation:[aUnit unitLocation]];
  
    if (target_city != nil)
    {
        Order *order;

        // Set orders to move to that coordinate.
        order = [[[OShortestMoveTo alloc] initForUnit:aUnit
                                          moveToLocation:[target_city cityLocation]
                                          onMap:[context map]] autorelease];
        [aUnit setOrder:order];
        [context setStatusLine:[aUnit statusLine]];
    }
}

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

- (void) goRandom:(Human *)context unit:(Unit *)aUnit
{
    [aUnit setOrder:[[[ORandom alloc] initForUnit:aUnit] autorelease]];
    [context setStatusLine:[aUnit statusLine]];
}

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

- (void) escortShip:(Human *)context
{
    [context saveState];
    [context changeState:HS_ESCORT_SHIP];
}

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

- (void) moveTo:(Human *)context
{
    [context saveState];
    [context changeState:HS_MOVE_TO];
}

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

- (void) patrolTo:(Human *)context
{
    [context saveState];
    [context changeState:HS_PATROL_TO];
}

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

- (void) explore:(Human *)context unit:(Unit *)unit
{
    [unit setOrder:[[[OExplore alloc] initForUnit:unit explore:[context map]] autorelease]];

    [self continueWithContext:context];
}

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

- (void) sentry:(Human *)context unit:(Unit *)aUnit
{
    [aUnit setOrder:[[[OSentry alloc] initForUnit:aUnit] autorelease]];

    [context setStatusLine:[aUnit statusLine]];
}

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

- (void) clearOrders:(Human *)context unit:(Unit *)aUnit
{
    [aUnit setOrder:nil];
    [context setStatusLine:[aUnit statusLine]];
}

//----------------------------------------------------------------------
// Clear orders, but don't activate units (i.e. do not put in the
// units awaiting movement list, even if they can move.)
//----------------------------------------------------------------------

- (void) clearOrders:(Human *)context city:(City *)aCity
{
    NSEnumerator *unitEnumerator = [[aCity cityUnits] objectEnumerator];
    int count = 0;
    Unit *unit;
    
    while (unit = [unitEnumerator nextObject])
    {
        count++;
        [unit setOrder:nil];
    }

    [context setStatusLine:[NSString stringWithFormat:@"Orders cleared for %d unit%@.", count, (count == 1) ? @"" : @"s"]];
}

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

- (void) loadShip:(Human *)context unit:(Unit *)aUnit
{
    UnitType unitType = [aUnit unitType];

    if (unitType == u_transport || unitType == u_carrier)
    {
        [aUnit setOrder:[[[OLoadShip alloc] initForUnit:aUnit] autorelease]];
        // May lead to inconsistency -- continueWithContext...
        //[context activateThisUnit:aUnit];

        [self continueWithContext:context];
    }
}

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

- (void) unloadShip:(Human *)context unit:(Unit *)aUnit
{
#if NYC
    [aUnit unloadShip];
    [context wait:nil];
#endif
}

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

- (void) skipMove:(Human *)context unit:(Unit *)aUnit
{
    [aUnit skipMove];
    [context setStatusLine:[aUnit statusLine]];
}
  
//======================================================================
// Interface Management
//======================================================================

- (BOOL) validateMenuItem:(Human *)context:(NSMenuItem *)menuCell
{
    SEL action = [menuCell action];
    BOOL valid = NO;
    //Unit *unit = [context selectedUnit];

    // Commands

    if (action == @selector (moveMode:))
    {
        //valid = ([context selectedUnit] == nil) ? NO : YES;
        valid = YES;
    }
    else if (action == @selector (surveyMode:))
    {
        valid = NO;
    }
    else if (action == @selector (groupMode:))
    {
        valid = NO;
    }
    else if (action == @selector (wait:))
    {
        valid = NO;
    }
    else if (action == @selector (flightPaths:))
    {
        valid = YES;
    }
    else if (action == @selector (activateUnit:))
    {
        //printf ("selectedUnit: %p\n", [context selectedUnit]);
        //valid = ([context selectedUnit] != nil && [[context selectedUnit] currentRange] > 0) ? YES : NO;
        valid = YES;
    }
    else if (action == @selector (centerScreen:)
             || action == @selector (centerCursor:))
    {
        //valid = [[context mapView] cursorEnabled];
        valid = YES;
    }
    else if (action == @selector (showProductionMap:)
             || action == @selector (showCombatReport:))
    {
        valid = YES;
    }

    // Orders
  
    else if (action == @selector (goDirection:)
             || action == @selector (goHome:)
             || action == @selector (goRandom:) // Not for fighter
             || action == @selector (moveTo:)
             || action == @selector (escortShip:) // Only if ship
             || action == @selector (sentry:) // Not for fighter not in city/carrier
             || action == @selector (clearOrders:) // Only if city or unit w/ order
             || action == @selector (loadShip:) // Only ship
             || action == @selector (unloadShip:) // Only ship
             || action == @selector (skipMove:) // Only if has movement left
             || action == @selector (explore:)
                )
    {
        //valid = ([context selectedUnit] == nil) ? NO : YES;
        valid = YES;
    }    
    else if (action == @selector (patrolTo:) )
    {
        //valid = (unit != nil && ([unit unitType] != u_fighter || [unit isOnBoardShip] == YES || [unit isInCity] == YES));
        valid = YES;
    }

    return valid;
}

@end
