#include <string.h>
#include "misc.h"
#include "defs.h"
#include "world.h"
#include "display.h"
#include "trans.h"
#include "units.h"
#include "move.h"
#include "city.h"
#include "MsgQ.h"
#include "rules.h"
#include "savefile.h"

#include "../pic/armor.xpm"
#include "../pic/artillery.xpm"
#include "../pic/battleship.xpm"
#include "../pic/bomber.xpm"
#include "../pic/cannon.xpm"
#include "../pic/caravan.xpm"
#include "../pic/carrier.xpm"
#include "../pic/catapult.xpm"
#include "../pic/cavalry.xpm"
#include "../pic/chariot.xpm"
#include "../pic/cruiser.xpm"
#include "../pic/diplomat.xpm"
#include "../pic/fighter.xpm"
#include "../pic/frigate.xpm"
#include "../pic/ironclad.xpm"
#include "../pic/knights.xpm"
#include "../pic/legion.xpm"
#include "../pic/mechinf.xpm"
#include "../pic/militia.xpm"
#include "../pic/musketeers.xpm"
#include "../pic/nuclear.xpm"
#include "../pic/phalanx.xpm"
#include "../pic/riflemen.xpm"
#include "../pic/sail.xpm"
#include "../pic/settler.xpm"
#include "../pic/submarine.xpm"
#include "../pic/transport.xpm"
#include "../pic/trireme.xpm"

char *names[] = {
  "Armor",
  "Artillery",
  "Battleship",
  "Bomber",
  "Cannon",
  "Caravan",
  "Carrier",
  "Catapult",
  "Cavalry",
  "Chariot",
  "Cruiser",
  "Diplomat",
  "Fighter",
  "Frigate",
  "Ironclad",
  "Knights",
  "Legion",
  "Mech. Infantry",
  "Militia",
  "Musketeers",
  "Nuclear",
  "Phalanx",
  "Riflemen",
  "Sail",
  "Settlers",
  "Submarine",
  "Transport",
  "Trireme"
  };

char **pictures[] = {
  armor_xpm,
  artillery_xpm,
  battleship_xpm,
  bomber_xpm,
  cannon_xpm,
  caravan_xpm,
  carrier_xpm,
  catapult_xpm,
  cavalry_xpm,
  chariot_xpm,
  cruiser_xpm,
  diplomat_xpm,
  fighter_xpm,
  frigate_xpm,
  ironclad_xpm,
  knights_xpm,
  legion_xpm,
  mechinf_xpm,
  militia_xpm,
  musketeers_xpm,
  nuclear_xpm,
  phalanx_xpm,
  riflemen_xpm,
  sail_xpm,
  settler_xpm,
  submarine_xpm,
  transport_xpm,
  trireme_xpm
  };

int admList[] = {
  10, 5, 3, // armor
  12, 2, 2, // artillery
  18, 12, 4,// battleship
  12, 1, 8, // bomber
  8, 1, 1,  // cannon
  0, 1, 1,  // caravan
  1, 12, 5, // carrier
  6, 1, 1,  // catapult
  2, 1, 2,  // cavalry
  4, 1, 2,  // chariot
  6, 6, 6,  // cruiser
  0, 0, 2,  // diplomat
  4, 2, 10, // fighter
  2, 2, 3,  // frigate
  4, 4, 4,  // ironclad
  4, 2, 2,  // knights
  3, 1, 1,  // legion
  6, 6, 3,  // mech. inf
  1, 1, 1,  // militia
  2, 3, 1,  // musketeers
  99, 0, 16,// nuclear
  1, 2, 1,  // phalanx
  3, 5, 1,  // riflemen
  1, 1, 3,  // sail
  0, 1, 1,  // settlers
  8, 2, 3,  // submarine
  0, 3, 4,  // transport
  1, 0, 3   // trireme
  };

long *Unit::compiledPicsArray[MaxUnits];

char **Unit::Picture()
{
  return pictures[type];
}

int UnitVisibility(int type)
{
  switch (type) {
  case CARRIER:
  case BATTLESHIP:
  case BOMBER:
  case CRUISER:
  case SUBMARINE:
    return 2;
  }
  return 1;
}

int UnitAttack(int type)
{
  return admList[type*3];
}

int UnitDefense(int type)
{
  return admList[type*3+1];
}

int UnitMoves(int type)
{
  return admList[type*3+2];
}

int SettlerOrder(Unit *u, int key)
{
  City *city;
  switch (key) {
  case 'b': // build city
    if (world->WhichCity(u->x, u->y) != 0) return 1;
    city = new City(strdup(*cityNames++), trans->New(), u->x, u->y, playerId);
    world->MakeRoad(u->x, u->y);
    *moveQ << PieceMove(city->id, CITY_CREATE, u->x, u->y);
    *moveQ << city->name;
    *moveQ << PieceMove(u->id, PIECE_DIE, 0, 0);
    return 0; // we died
  case 'i': // irrigate
    if ((u->orderProgress = world->IrrigTime(u->x, u->y)) < 0) break; // can't
    u->currOrder = 'i';
    u->numMoves = 0;
    break;
  case 'r': // road
    if (world->IsRoad(u->x, u->y)) { // try to build railroad
      if (Discovered(u->ownerId, "Railroad") &&
	  (u->orderProgress = world->RailRoadTime(u->x, u->y)) > 0) {
	u->currOrder = 'r';
	u->numMoves = 0;
      }
      break;
    }
    if (world->IsRailRoad(u->x, u->y)) break; // can't do more
    if (world->Terrain(u->x, u->y) == RIVER &&
	!Discovered(u->ownerId, "Bridge Building")) break;
    if ((u->orderProgress = world->RoadTime(u->x, u->y)) > 0) {
      u->currOrder = 'r';
      u->numMoves = 0;
    }
    break;
  case 'm':
    if ((u->orderProgress = world->MineTime(u->x, u->y)) > 0) {
      u->currOrder = 'm';
      u->numMoves = 0;
    }
    break;
  }
  return 1;
}

void SettlerCompleteOrder(Unit *u)
{
  switch (u->currOrder) {
  case 'i':
    if (--u->orderProgress <= 0) {
      world->Irrigate(u->x, u->y);
      u->currOrder = ' ';
    }
    u->numMoves = 0;
    break;
  case 'r':
    if (--u->orderProgress <= 0) {
      world->MakeRoad(u->x, u->y);
      u->currOrder = ' ';
    }
    u->numMoves = 0;
    break;
  case 'm':
    if (--u->orderProgress <= 0) {
      world->Mine(u->x, u->y);
      u->currOrder = ' ';
    }
    break;
  }
}    

Unit *NewUnit(int type, int owner, ulong id, int vet, ulong city, int x, int y)
{
  Debug('u', "Creating new Unit id %ld, owner %d, at %d %d\n", id, owner, x, y);
  Unit *unit = new Unit(owner, id, vet, city, x, y, type);
  trans->SetPtr(unit->id, unit);
  if (owner == playerId) {
    display->ShowPiece(unit->x, unit->y, unit->id);
    world->SeeSquare(unit->x, unit->y, unit->Visibility());
  }
  else
    unit->DrawIfVisible();
  return unit;
}

int Unit::Order(int key)
{
  if (key == 'f') {
    currOrder = 'f';
    numMoves = 0;
  }
  else if (key == 's') {
    currOrder = 's';
    numMoves = 0;
  }
  else if (key == ' ')
    numMoves = 0;
  else if (key == 'u' && carrying) { // wake up cargo
    for (Lister<ulong> l = carrying; l; l.Next()) {
      Unit *ptr = trans->TransUnit(l.Elem());
      ptr->currOrder = ' ';
      ptr->numMoves = ptr->Moves();
    }
  }
  else {
    switch (type) {
    case SETTLER:
      return SettlerOrder(this, key);
    }
  }
  return 1;
}

void Unit::CompleteOrder()
{
  if (currOrder == 'f') {
    if (!Fortified()) {
      Fortify();
      DrawIfVisible();
      *moveQ << PieceMove(id, UNIT_UPDATE, 1, Veteran() ? 1 : 0);
    }
    numMoves = 0;
  }
  else if (currOrder == 's') // asleep
    numMoves = 0;
  else {
    switch (type) {
    case SETTLER:
      return SettlerCompleteOrder(this);
    }
  }
}

int SeaUnit(int type)
{
  switch (type) {
  case BATTLESHIP:
  case CARRIER:
  case CRUISER:
  case FRIGATE:
  case IRONCLAD:
  case SAIL:
  case SUBMARINE:
  case TRANSPORT:
  case TRIREME:
    return 1;
  }
  return 0;
}

int AirUnit(int type)
{
  switch (type) {
  case FIGHTER:
  case BOMBER:
    return 1;
  }
  return 0;
}

int Unit::CanMoveOn(int terrainType, int dx, int dy)
{
  // can always move into a city
  if (world->WhichCity(dx, dy) != 0 || type == NUCLEAR) return 1;
  if (SeaUnit(type)) return terrainType == WATER;
  if (AirUnit(type)) return 1;
  if (terrainType == WATER) { // check if there is a carrier unit
    // land unit can't move from carrier to carrier
    // so if its already carried fail
    if (carriedBy != 0) return 0;
    for (Lister<ulong> unitl = world->Units(dx, dy); unitl; unitl.Next()) {
      Unit *unit = trans->TransUnit(unitl.Elem());
      if (unit->CanCarry(this))
	return 1;
    }
    return 0;
  }
  return 1;
}
    
int Unit::CanAttack(int type)
{
  // sea units can attack other sea units
  // battleships, cruisers, ironclads, frigates can attack coastal units
  switch (type) {
  case BATTLESHIP:
  case IRONCLAD:
  case FRIGATE:
  case CRUISER:
    if (AirUnit(type) || type == NUCLEAR) return 0;
    return 1;
  case CARRIER:
  case SAIL:
  case SUBMARINE:
  case TRIREME:
  case TRANSPORT:
    return SeaUnit(type);
  case FIGHTER:
    return type == BOMBER;
  case BOMBER: // can attack anything except a nuclear or fighter
    if (AirUnit(type) || type == NUCLEAR) return 0;
    return 1;
  }
  if (AirUnit(type) || SeaUnit(type) || type == NUCLEAR || carriedBy != 0)
    return 0;
  return 1;
}

double Unit::MoveCost(int terrainType, double movesRem, int sx, int sy,
		      int dx, int dy)
{
  double cost;
  if (world->IsRoad(sx, sy) && world->IsRoad(dx, dy))
    cost = 0.1;
  else if (world->IsRailRoad(sx, sy) && world->IsRailRoad(dx, dy))
    cost = 0;
  else {
    switch (terrainType) {
    case MOUNTAINS:
      cost = 3; break;
    case SWAMP:
    case HILLS:
    case FOREST:
    case JUNGLE:
      cost = 2; break;
    case DESERT:
    case PLAINS:
    case GRASS:
    case GRASS1:
    case RIVER:
      cost = 1; break;
    case WATER:
      if (SeaUnit(type) || AirUnit(type)) cost = 1;
      else cost = movesRem;
      break;
    default:
      cost = 1;
      break;
    }
  }
  if (cost > movesRem) {
    if (rand()%10 < 2)
      return -1.0; // can't move, too high cost
    return movesRem;
  }
  return cost;
}

int Unit::CanCarry(Unit *unit)
{
  if (unit->ownerId != ownerId) return 0;
  int carryNum = 0, destType = unit->type;
  switch (type) {
  case CARRIER:
    return (AirUnit(destType) || destType == NUCLEAR) && carrying.Count() < 12;
  case FRIGATE:
    carryNum = 4; break;
  case SAIL:
    carryNum = 3; break;
  case TRANSPORT:
    carryNum = 8; break;
  case TRIREME:
    carryNum = 2; break;
  }
  return carryNum > 0 && !AirUnit(destType) && !SeaUnit(destType) &&
    destType != NUCLEAR && carrying.Count() < carryNum;
}

void Unit::SetCoord(int xc, int yc)
{
  x = xc;
  y = yc;
  for (Lister<ulong> l = carrying; l; l.Next())
    trans->TransUnit(l.Elem())->SetCoord(x, y);
}

void Unit::Save()
{
  WriteULong(id);
  WriteULong(cityId);
  WriteULong(carriedBy);
  WriteUChar(flags);
  WriteUChar(ownerId);
  WriteUShort(x);
  WriteUShort(y);
  WriteUChar(type);
  WriteUChar(currOrder);
  WriteUShort(orderProgress); 
  WriteUShort(carrying.Count());
  for (Lister<ulong> l = carrying; l; l.Next())
    WriteULong(l.Elem());
}
