// -*- c++ -*-
#ifndef _UNITS_H
#define _UNITS_H

#include "global.h"
#include "world.h"
#include "coord.h"
#include "pixmaps.h"

class MsgQ;

int UnitVisibility(BuildObject type);
int UnitAttack(BuildObject type);
int UnitDefense(BuildObject type);
int UnitMoves(BuildObject type);
int SeaUnit(BuildObject type);
int AirUnit(BuildObject type);
int CarryCapacity(BuildObject type);

class Unit
{
public:
  Unit(uchar OwnerId, ulong Id, int Veteran, ulong CityId, MapCoord where,
       BuildObject unitType);
  Unit(); // read from save file
  ~Unit();
  
  ulong id, cityId;
  ulong carriedBy; // 0 if not carried
  uchar flags;
  uchar ownerId;
  
  MapCoord coord;
  BuildObject type; // unit type
  double numMoves; // at each turn we fill this in with the movement points
  
  // these are used if the unit is ordered to do something
  // like irrigation or building a road, etc
  char currOrder;
  int orderProgress;
  
  List<ulong> carrying;
  
  enum { FORTIFIED = 0x01, VETERAN = 0x02, SLEEPING = 0x04, MUST_LAND = 0x08 };
  
  int Fortified() { return flags & FORTIFIED; }
  int Veteran() { return flags & VETERAN; }
  int Sleeping() { return flags & SLEEPING; }
  
  void Fortify() { flags |= FORTIFIED; }
  void Sleep() { flags |= SLEEPING; }
  void MakeVeteran() { flags |= VETERAN; }
  
  void UnFortify() { flags &= ~FORTIFIED; }
  void Wake() { flags &= ~SLEEPING; }
  
  BuildObject Type() { return type; }
  
  int Attack() {return int(UnitAttack(type)*(Veteran() ? 1.5 : 1)+0.5);}
  
  int Defense(BuildObject against) {
    return int(UnitDefense(type)*(Veteran() ? 1.5 : 1)*
	       (Fortified() ? 1.5 : 1)*world->SquareDefense(coord, against)+0.5);
  }
  
  int Moves(int Bonus) { 
    return int(SeaUnit(type) ? UnitMoves(type)+Bonus : UnitMoves(type));
  }
  
  int Visibility() { return UnitVisibility(type); }
  
  // true if the unit can carry us, checks if owners match too
  int CanCarry(Unit *);
  
  void SetCoord(MapCoord c);
  void SetHomeCity(int CityId);
  
  List<ulong> &Carrying() { return carrying; }

  // if this fails that means the unit died
  int Order(int key);

  // returns 0 if the unit dies
  int EndTurn();

  // after you fill in the moves call this function
  // if the unit is processing an order this will make it
  // do the work
  void CompleteOrder(MapDisplay *display = MainMap);

  // true if we can attack the given unit type
  int CanAttack(BuildObject type);

  // true if it can move on the given terrain type
  int CanMoveOn(int terrainType, MapCoord dest);

  // true if it can attack on the given terrain type
  int CanAttackOn(int terrainType);

  // returns the movement cost, if it is -ve then the piece loses all
  // its movement points and doesn't get to move
  double MoveCost(int terrainType, double movesRem, MapCoord dest);

  void Draw(WDisplay *window);
  void Draw(WDisplay *window, ScreenCoord c);
  void ShowMoveTo(MapDisplay *display, MapCoord newCoord);

  CPixmap *Pic() {return Units[type].picture;}

  friend int MoveUnit(Unit *, MapCoord newCoord, double cost, MsgQ *outQ, MapDisplay *display = MainMap);

  void DrawIfVisible(MapDisplay *display = MainMap);

  void Save();
};

Unit *NewUnit(BuildObject type, int owner, ulong id, int vet, ulong city,
	      MapCoord where, MapDisplay *display = MainMap);

#endif
