#include <string.h>
#include <math.h>

#ifdef USE_X11
# include <sys/types.h>
# include <netinet/in.h>
#else
# ifdef MS_WIN
#  include <winsock.h>
# endif
#endif

#include "city.h"
#include "defs.h"
#include "global.h"

#include "misc.h"
#include "graph.h"
#include "units.h"
#include "world.h"
#include "rivers.h"
#include "MsgQ.h"
#include "player.h"
#include "trans.h"
#include "display.h"
#include "move.h"
#include "rules.h"
#include "hash.h"
#include "savefile.h"
#include "mapdisplay.h"
#include "citydisplay.h"

// moved from net.cc
char *initCityNames[] = {
  "Ludhiana", "Amritsar", "Tarn Tarn", "Phillaur", "Banga",
  "Nawasher", "Malerkotla", "Ambala", "Chandigarh", "Nahan",
  "Nearwhon", "Balnish", "Orinti", "Vintar", "Zorina",
  "Optalla", "Missing 1", "Missing 2", "Missing 3", "Missing 4",
  // FIXME: 4 missing city names here !!
  // next players
  "Delhi", "Gurgaon", "Meerut", "Lucknow", "Jaipur",
  "Agra", "Hyderabad", "Calcutta", "Bombay", "Banaras",
  "Mishni", "Karnak", "Bangladish", "Vishnar", "Mirikki",
  "Malabad", "Zanzibar", "Kumquat", "Hirichi", "Aumbar",
  // next
  "Lahore", "Karachi", "Islamabad", "Sind", "Kabul",
  "Tehran", "Baghdad", "Corinni", "Zanzi", "Morikki",
  "Genarik", "Tornak", "Akkabad", "Nintar", "Toran",
  "Ekbar", "Allibad", "Jeembad", "Harah", "Targistan",
  // next
  "Lyndynn", "Chester", "Oxford", "Cambridge", "Plymouth",
  "Manchester", "Birmingham", "Leeds", "Cheltenham", "Darby",
  "Epsom", "Brighton", "Windsor", "Epping", "Soho",
  "Tyne", "Lancaster", "York", "Wharwick", "Hampton",
};

City::City(char *Name, ulong Id, MapCoord where, uchar OwnerId)
{
  name = Name;
  coord = where;
  id = Id;
  trans->SetPtr(id, this);
  ownerId = OwnerId;
  players[ownerId]->citys.Insert(id);
  size = 1;
  goondas = 0; happy = 1;
  taxmen = scientists = elvi = 0;
  
  if (players[ownerId]->citys.Count() == 1)
    {
      AddBuilding(PALACE);
      players[ownerId]->capital = this;
    }
  
  building = SETTLER;
  accProd = 0;

  foodStored = 0;
  settlers = 0;
  LoveTheRuler = 0;

  display = NULL;
  world->WhichCity(coord) = id;
  if (ownerId == playerId) world->SeeSquare(MainMap, coord, CITY_VISIBILITY);
  world->Working(coord) = id;
  DrawIfVisible(MainMap);
  PutExtraWorker();
  if (ownerId == playerId)
    ComputeEcon();
}

// read from save file
City::City()
{
  display = NULL;
  goondas = 0; happy = 1; settlers = 0;
  id = ReadULong();
  name = ReadString();
  coord.x = ReadUShort();
  coord.y = ReadUShort();
  ownerId = ReadUChar();
  size = ReadUChar();
  building = BuildObject(ReadUChar());
  accProd = ReadUShort();
  foodStored = ReadUShort();
  scientists = ReadUShort();
  taxmen = ReadUShort();
  LoveTheRuler = ReadUShort();

  buildings.Restore();
  wonders.Restore();

  trans->SetPtr(id, this);
  players[ownerId]->citys.Insert(id);
  world->WhichCity(coord) = id;
}

City::~City()
{
  MapCoord c;
  for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
    if ((xi % 4) == 0 && (yi % 4) == 0) continue;
    c = coord.down(yi-2).left(xi-2);
    if (world->Working(c) == id)
      world->Working(c) = 0;
  }
  if (players[ownerId]->capital == this)
    players[ownerId]->capital = NULL;

  // kill units
  while (units) {
    Unit *unit = trans->TransUnit(units.First());
    *moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
    delete unit; // destructor takes care of everything else
  }

  // remove wonders
  BuildObject b;
  FOR_ALL_WONDERS(b)
    if (HasBuilding(b)) 
      DeleteBuilding(b);
  
  world->WhichCity(coord) = 0;
  players[ownerId]->citys.Delete(id);
  delete name;
  DrawIfVisible(MainMap);
  if (id == playerId)
    world->HideSquare(MainMap, coord, CITY_VISIBILITY);
}

void City::DeleteBuilding(BuildObject b)
{
  if (Objects[b].IsBuilding())
    buildings.Delete(b);
  else if (Objects[b].IsWonder()) {
    wonders.Delete(b);
    players[ownerId]->DeleteWonder(b);
    // should notify everyone that its been destroyed
  }
}

void City::AddBuilding(BuildObject b)
{
  if (Objects[b].IsBuilding())
    buildings.Insert(b);
  else if (Objects[b].IsWonder()) {
    wonders.Insert(b);
    players[ownerId]->InsertWonder(b);
  }
}

int City::HasBuilding(BuildObject b)
{
  if (Objects[b].IsBuilding())
    return buildings.Contains(b);
  else if (Objects[b].IsWonder())
    return wonders.Contains(b);
  return 0;
}

void City::AddUnit(int id, BuildObject type)
{
  units.Insert(id);
  if (type == SETTLER)
    settlers++;
}

void City::RemoveUnit(int id, BuildObject type)
{
  units.Delete(id);
  if (type == SETTLER)
    settlers--;
}

void City::Draw(WDisplay *window)
{
  window->DrawCity(coord, players[ownerId], size, HasBuilding(CITYWALLS), goondas > happy);
}

// find an empty spot and put work on it
void City::PutExtraWorker()
{
  int f = -1, p = -1, t = -1;
  MapCoord c, mc;
  for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
    if ((xi % 4) == 0 && (yi % 4) == 0) continue;
    c = coord.down(yi-2).right(xi-2);
    if (world->RealVisible(c) == 0) continue;
    if (world->Working(c) != 0) continue;
    int cf = world->Food(c, ownerId, LoveTheRuler);
    int cp = world->Prod(c, ownerId, LoveTheRuler);
    int ct = world->Trade(c, ownerId, LoveTheRuler);
    if (cf > f || (cf == f && (ct > t || (ct == t && cp > p)))) {
      f = cf; p = cp; t = ct;
      mc = c;
    }
  }
  if (f != -1)
    world->Working(mc) = id;
}

// find a spot and pollute it
void City::PutPollution()
{
  world->PolluteNear(coord);
  AddMessage(messages,"Pollution near %s",name);
}

void City::ComputeEcon()
{
  MapCoord c;
  Debug('c', "Computing economy for city %s\n", name);
  prod = food = trade = pollution = working = 0;
  for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
    if ((xi % 4) == 0 && (yi % 4) == 0) continue;
    c = coord.down(yi-2).right(xi-2);
    if (world->Working(c) != id) continue;
    ulong enemyUnit = 0;
    for (Lister<ulong> unitl = world->Units(c); unitl; unitl.Next())
      if (trans->TransUnit(unitl.Elem())->ownerId != ownerId)
	enemyUnit = unitl.Elem();
    if (enemyUnit != 0) {
      world->Working(c) = 0; // can't work here
      continue;
    }
    if (working == size && c != coord) {
      world->Working(c) = 0;
      continue;
    }
    food += world->Food(c, ownerId, LoveTheRuler);
    prod += world->Prod(c, ownerId, LoveTheRuler);
    int val = world->Trade(c, ownerId, LoveTheRuler);
    if (val > 0)
      val += players[ownerId]->TradeBonus();
    trade += val;
    if (c != coord) ++working;
  }
  Player *p = players[ownerId];
  elvi = size-working-scientists-taxmen;
  if (elvi < 0) {
    if (taxmen > 0) {
      if (taxmen >= -elvi) {
	taxmen += elvi;
	elvi = 0;
      }
      else {
	elvi += taxmen;
	taxmen = 0;
      }
    }
    if (elvi < 0) {
      scientists+=elvi;
      elvi=0;
    }
  }

  double lux_factor = 1, money_factor = 1, science_factor = 1;
  goondas = size - FREE_CONTENTED;
  happy = 0;
  food -= 2*size;
  food -= foodSupport();

  if (p->govt < REPUBLIC) // martial order
    goondas -= world->Units(coord).Count();

  if (players[ownerId]->UnhappyPerUnitAway())
    for (Lister<ulong> l = units;l;l.Next()) {
      Unit *unit = trans->TransUnit(l.Elem());
      if (unit->coord != coord)
	if ((unit->type != SETTLER) && (unit->type != DIPLOMAT) && (unit->type != CARAVAN))
	  goondas += (players[ownerId]->UnhappyPerUnitAway());
    }
  
  // calculate corruption
  if (players[ownerId]->govt == DEMOCRACY)
    corruption = 0;
  else {
    if (players[ownerId]->capital != NULL)
      corruption = int(trade*0.7*(1.0-exp((-1)*world->Distance(coord,players[ownerId]->capital->coord)/20.0)));
    else
      corruption = int(trade*0.7);

  if (HasBuilding(COURTHOUSE))
    corruption /= 2;
  }

  if (HasBuilding(CATHEDRAL))
    goondas -= p->CathedralEffect();
  if (HasBuilding(COLOSSEUM))
    goondas -= 3;
  if (HasBuilding(TEMPLE))
    goondas -= p->TempleEffect();

  if (HasBuilding(LIBRARY))
      science_factor *= 1.5;
  if (HasBuilding(UNIVERSITY))
    if (p->HasWonder(ISAACNEWTON))
      science_factor *= 2;
    else
      science_factor *= 1.5;

  if (HasBuilding(MARKETPLACE)) {
    lux_factor *= 1.5;
    money_factor *= 1.5;
  }
  if (HasBuilding(BANK)) {
    lux_factor *= 1.5;
    money_factor *= 1.5;
  }

  if (HasBuilding(MFGPLANT))
    prod = prod*2;
  else if (HasBuilding(FACTORY))
    prod = int(prod*1.5);

  if ((HasBuilding(MFGPLANT) || HasBuilding(FACTORY)) &&
      ((HasBuilding(POWERPLANT) || HasBuilding(HYDROPLANT)|| 
	HasBuilding(FUSIONPLANT)|| HasBuilding(NUCLEARPLANT) || 
	p->HasWonder(HOOVERDAM))))
    prod = int(prod*1.5);

  // wonders
  if (p->HasWonder(CUREFORCANCER))
    happy++;
  if (p->HasWonder(HANGINGGARDENS) && !Objects[HANGINGGARDENS].isObsolete)
    happy++;
  if (p->HasWonder(JSBACH))
    happy += 2;
  if (p->HasWonder(SETIPROGRAM))
    science_factor *= 1.5;
  if (HasBuilding(COPERNICUS) && !Objects[COPERNICUS].isObsolete)
    science_factor *= 1.5;
 
  // pollution
  pollution = 0;
  if ((HasBuilding(POWERPLANT)) && !(HasBuilding(HYDROPLANT)|| 
		   HasBuilding(FUSIONPLANT)|| HasBuilding(NUCLEARPLANT) || 
		    p->HasWonder(HOOVERDAM))) // dirtyplant
    pollution += prod;
  if (HasBuilding(RECYCLINGCTR))
    pollution /= 2;
  
  if (!HasBuilding(MASSTRANSIT)) { 
    if (players[ownerId]->HasDiscovered(AUTOMOBILE))
      pollution += size;
    else
      if (size > 10)
	pollution += size - 10;
  }

  money = (trade-corruption)*p->tax/100;
  luxuries = (trade-corruption)*p->luxuries/100;
  science = trade-corruption-money-luxuries;
  luxuries += 2*elvi;
  science += 2*scientists;
  money += 2*taxmen;
  luxuries = int(luxuries*lux_factor);
  science = int(science*science_factor);
  money = int(money*money_factor);

// each 2 luxuries reduces goondas by one,
// when run out of goondas actually create happy instead:
  if (goondas<0) goondas = 0;
  goondas -= int(luxuries/2);
  if (goondas < 0) {
     happy-= goondas;
     goondas = 0;
  }

  if (HasBuilding(SHAKESPEARE) && !Objects[SHAKESPEARE].isObsolete)
    goondas = 0;

// prod, money and science zeroed if goondas > happy:
  if (disordered()) {
    prod = 0;
    money = 0;
    science = 0;
  }
}

int City::Update()
{
  // size zero = city dies:
  if (size <= 0) return 0;
  if ((Objects[building].isObsolete) || Objects[building].built)
    building = NOOBJECT;

  ComputeEcon();
  Debug('c', "Updating city %s\n", name);
  while (surpProd() < 0) { // kill a unit
    Unit *unit = trans->TransUnit(units.First());
    AddMessage(messages, "%s can't support %s", name, Objects[unit->Type()].name);
    *moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
    delete unit; // automatically removes it from city
  }
  accProd += surpProd();
  foodStored += food;
  if (foodStored < 0) { // kill off settlers
    for (Lister<ulong> unitl = units; unitl && foodStored < 0;) {
      Unit *unit = trans->TransUnit(unitl.Elem());
      if (unit->type == SETTLER) {
	AddMessage(messages, "%s can't feed Settlers", name);
	*moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
	delete unit;
	unitl = units;
      }
      else
	unitl.Next();
    }
  }
  if (foodStored < 0) {
    foodStored = 0;
    --size;
    AddMessage(messages, "Famine in %s, population decrease", name);
    if (size <= 0) // city dies
      return 0;
    else
      *moveQ << PieceMove(id, CITY_SIZE, size, 0);
    DrawIfVisible(MainMap);
  }
  if (foodStored >= foodStoreCap()) { // eat food, maybe grow city
    // check for granary to retain half of foodStored:
    if (HasBuilding(GRANARY)) foodStored -= (int) foodStoreCap()/2;
    else foodStored -= foodStoreCap();
    // cannot pass size 10 without aquaduct
    if ((size >= 10) && !HasBuilding(AQUADUCT)) {
      AddMessage(messages, "%s needs aquaduct to grow", name);
    } else {
      ++size;
      *moveQ << PieceMove(id, CITY_SIZE, size, 0);
      PutExtraWorker();
      DrawIfVisible(MainMap);
    }
  }
  if (accProd >= needProd()) {
    Produce();
  }
  ComputeEcon();
  if (random() % 100 +1 <= pollution) {
    PutPollution();
  }
// notification of civil disorder:
// also, collapse of democracy into anarchy if any city does civil-disorder.
  if (goondas > happy) {
     AddMessage(messages, "Civil disorder in %s", name);
     if (players[ownerId]->govt == DEMOCRACY)
	players[ownerId]->govt = ANARCHY;
  }

  if ((size > 3) && (!goondas) && (happy >= size-happy-goondas)) {
    if (!LoveTheRuler) {
      AddMessage(messages,"We love the %s Day celebrated in %s",players[ownerId]->title(),name);
      LoveTheRuler = 1;
    }
    else if ((players[ownerId]->govt >= REPUBLIC) && (food > 0)) {
      if ((size >= 10) && !HasBuilding(AQUADUCT)) {
	AddMessage(messages, "%s needs aquaduct to grow", name);
      } else {
	++size;
	*moveQ << PieceMove(id, CITY_SIZE, size, 0);
	PutExtraWorker();
	DrawIfVisible(MainMap);
      }
    }
  }
  else
    if (LoveTheRuler) {
      AddMessage(messages,"We love the %s Day cancelled in %s",players[ownerId]->title(),name);
      LoveTheRuler = 0;
    }

  players[ownerId]->money += money;
  players[ownerId]->scienceAcc += science;

  BuildObject b;
  FOR_ALL_BUILDINGS(b)
    if (HasBuilding(b)) {
      players[ownerId]->money -= Objects[b].maint;
      if (players[ownerId]->money < 0) {
	players[ownerId]->money += Objects[b].SellValue();
	AddMessage(messages, "%s can't maintain %s", name, Objects[b].name);
	DeleteBuilding(b);
	if (b == CITYWALLS)
	  *moveQ << PieceMove(id,CITY_WALL,0,0);
	if (b == SDIDEFENSE)
	  *moveQ << PieceMove(id,SDI_DEFENSE,0,0);
      }
    }
  if (display != NULL)
    display->Update();

  Debug('c', "Done updating city %s\n", name);
  return 1;
}

int
City::HasCoast(void)
{
  for (int i = -1;i<1;i++)
    for (int j = -1;j<1;j++)
      if (world->Terrain(coord.up(i).left(j)) == WATER)
	return 1;
  return 0;
}
