#include "rules.h"
#include "misc.h"
#include "hash.h"
#include "list.h"
#include "units.h"
#include "player.h"
#include "savefile.h"
#include <string.h>

UnitData Units[LASTUNIT+1];
ObjectData Objects[MAXOBJECTS];
ScienceData Sciences[FUTURETECH];

ScienceData science_list[] =  {
  {"Advanced Flight", ADVANCEDFLIGHT, {FLIGHT, ELECTRICITY }},
  {"Alphabet", ALPHABET, {SURVIVAL, SURVIVAL }},
  {"Astronomy", ASTRONOMY, {MYSTICISM, MATHEMATICS }},
  {"Atomic Theory", ATOMICTHEORY, {THEORYOFGRAVITY, PHYSICS }},
  {"Automobile", AUTOMOBILE, {COMBUSTION, STEEL }},
  {"Banking", BANKING, {TRADE, THEREPUBLIC }},
  {"Bridge Building", BRIDGEBUILDING, {IRONWORKING, CONSTRUCTION }},
  {"Bronze Working" , BRONZEWORKING, {SURVIVAL, SURVIVAL }},
  {"Ceremonial Burial", CEREMONIALBURIAL, {SURVIVAL, SURVIVAL }},
  {"Chemistry", CHEMISTRY, {UNIVERSITIES, MEDICINE }},
  {"Chivalry", CHIVALRY, {FEUDALISM, HORSEBACKRIDING }},
  {"Code of Laws", CODEOFLAWS, {ALPHABET, SURVIVAL }},
  {"Combustion", COMBUSTION, {REFINING, EXPLOSIVES }},
  {"Communism", COMMUNISM, {PHILOSOPHY, INDUSTRIALIZATION }},
  {"Computers", COMPUTERS, {MATHEMATICS, ELECTRICITY }},
  {"Conscription", CONSCRIPTION, {THEREPUBLIC, EXPLOSIVES }},
  {"Construction", CONSTRUCTION, {MASONRY, CURRENCY }},
  {"Currency", CURRENCY, {BRONZEWORKING, SURVIVAL }},
  {"Democracy", DEMOCRACY, {PHILOSOPHY, LITERACY }},
  {"Electricity", ELECTRICITY, {METALLURGY, MAGNETISM }},
  {"Electronics", ELECTRONICS, {ENGINEERING, ELECTRICITY }},
  {"Engineering", ENGINEERING, {THEWHEEL, CONSTRUCTION }},
  {"Explosives", EXPLOSIVES, {GUNPOWDER, CHEMISTRY }},
  {"Feudalism", FEUDALISM, {MASONRY, MONARCHY }},
  {"Flight", FLIGHT, {COMBUSTION, PHYSICS }},
  {"Fusion Power", FUSIONPOWER, {NUCLEARPOWER, SUPERCONDUCTOR }},
  {"Genetic Engineering", GENETICENGINEERING, {MEDICINE, THECORPORATION }},
  {"Gunpowder", GUNPOWDER, {INVENTION, IRONWORKING }},
  {"Horseback Riding", HORSEBACKRIDING, {SURVIVAL, SURVIVAL }},
  {"Industrialization", INDUSTRIALIZATION, {RAILROAD, BANKING }},
  {"Invention", INVENTION, {ENGINEERING, LITERACY }},
  {"Iron Working", IRONWORKING, {BRONZEWORKING, SURVIVAL }},
  {"Labor Union", LABORUNION, {MASSPRODUCTION, COMMUNISM }},
  {"Literacy", LITERACY, {WRITING, CODEOFLAWS }},
  {"Magnetism", MAGNETISM, {NAVIGATION, PHYSICS }},
  {"Map Making", MAPMAKING,  {ALPHABET, SURVIVAL }},
  {"Masonry", MASONRY, {SURVIVAL, SURVIVAL }},
  {"Mass Production", MASSPRODUCTION, {AUTOMOBILE, THECORPORATION }},
  {"Mathematics", MATHEMATICS, {ALPHABET, MASONRY }},
  {"Medicine", MEDICINE, {PHILOSOPHY, TRADE }},
  {"Metallurgy", METALLURGY, {GUNPOWDER, UNIVERSITIES }},
  {"Monarchy", MONARCHY, {CEREMONIALBURIAL, CODEOFLAWS }},
  {"Mysticism", MYSTICISM, {CEREMONIALBURIAL, SURVIVAL }},
  {"Navigation", NAVIGATION, {MAPMAKING, ASTRONOMY }},
  {"Nuclear Fission", NUCLEARFISSION, {MASSPRODUCTION, ATOMICTHEORY }},
  {"Nuclear Power", NUCLEARPOWER, {NUCLEARFISSION, ELECTRONICS }},
  {"Philosophy", PHILOSOPHY, {MYSTICISM, LITERACY }},
  {"Physics", PHYSICS, {MATHEMATICS, NAVIGATION }},
  {"Plastics", PLASTICS, {REFINING, SURVIVAL }},
  {"Pottery", POTTERY, {SURVIVAL, SURVIVAL }},
  {"Railroad", RAILROAD, {BRIDGEBUILDING, STEAMENGINE }},
  {"Recycling", RECYCLING, {MASSPRODUCTION, DEMOCRACY }},
  {"Refining", REFINING, {CHEMISTRY, THECORPORATION }},
  {"Religion", RELIGION, {PHILOSOPHY, WRITING }},
  {"Robotics", ROBOTICS, {PLASTICS, COMPUTERS }},
  {"Rocketry", ROCKETRY, {ADVANCEDFLIGHT, ELECTRONICS }},
  {"Steam Engine", STEAMENGINE, {PHYSICS, INVENTION }},
  {"Steel", STEEL, {METALLURGY, INDUSTRIALIZATION }},
  {"Superconductor", SUPERCONDUCTOR, {PLASTICS, MASSPRODUCTION }},
  {"Survival", SURVIVAL, {SURVIVAL, SURVIVAL }},
  {"The Corporation", THECORPORATION, {BANKING, INDUSTRIALIZATION }},
  {"The Republic", THEREPUBLIC, {CODEOFLAWS, LITERACY }},
  {"The Wheel", THEWHEEL, {SURVIVAL, SURVIVAL }},
  {"Theory of Gravity", THEORYOFGRAVITY, {ASTRONOMY, UNIVERSITIES }},
  {"Trade", TRADE, {CURRENCY, CODEOFLAWS }},
  {"University", UNIVERSITIES, {MATHEMATICS, PHILOSOPHY }},
  {"Writing", WRITING, {ALPHABET, SURVIVAL }},
  {"Nothing", NOSCIENCE, {NOSCIENCE,NOSCIENCE}}
};
// 64 sciences

ObjectData object_list[] = {
  // name, type, unit, cost, maint, needed science, mask (assigned later)
  { "Armor", ARMOR, UNIT, 80, 0, AUTOMOBILE, 0, 0, NULL},
  { "Artillery", ARTILLERY, UNIT, 60, 0, ROBOTICS, 0, 0, NULL},
  { "Battleship", BATTLESHIP, UNIT, 160, 0, STEEL, 0, 0, NULL},
  { "Bomber", BOMBER, UNIT, 120, 0, ADVANCEDFLIGHT, 0, 0, NULL},
  { "Cannon", CANNON, UNIT, 40, 0, METALLURGY, 0, 0, NULL},
  { "Caravan", CARAVAN, UNIT, 50, 0, TRADE, 0, 0, NULL},
  { "Carrier", CARRIER, UNIT,160, 0, ADVANCEDFLIGHT, 0, 0, NULL},
  { "Catapult", CATAPULT, UNIT, 40, 0, MATHEMATICS, 0, 0, NULL},
  { "Cavalry", CAVALRY, UNIT, 20, 0, HORSEBACKRIDING, 0, 0, NULL},
  { "Chariot", CHARIOT, UNIT, 40, 0, THEWHEEL, 0, 0, NULL},
  { "Cruiser", CRUISER, UNIT, 80, 0, COMBUSTION, 0, 0, NULL},
  { "Diplomat", DIPLOMAT, UNIT, 30, 0, WRITING, 0, 0, NULL},
  { "Fighter", FIGHTER, UNIT, 60, 0, FLIGHT, 0, 0, NULL},
  { "Frigate", FRIGATE, UNIT, 40, 0, MAGNETISM, 0, 0, NULL},
  { "Ironclad", IRONCLAD, UNIT, 60, 0, STEAMENGINE, 0, 0, NULL},
  { "Knights", KNIGHTS, UNIT, 40, 0, CHIVALRY, 0, 0, NULL},
  { "Legion", LEGION, UNIT, 20, 0, IRONWORKING, 0, 0, NULL},
  { "Mech. Infantry", MECH_INF, UNIT, 50, 0, LABORUNION, 0, 0, NULL},
  { "Militia", MILITIA, UNIT, 10, 0, SURVIVAL, 0, 0, NULL},
  { "Musketeers", MUSKETEERS, UNIT, 30, 0, GUNPOWDER, 0, 0, NULL},
  { "Nuclear", NUCLEAR, UNIT, 160, 0, NUCLEARFISSION, 0, 0, NULL},
  { "Phalanx", PHALANX, UNIT, 20, 0, BRONZEWORKING, 0, 0, NULL},
  { "Riflemen", RIFLEMEN, UNIT, 30, 0, CONSCRIPTION, 0, 0, NULL},
  { "Sail", SAIL, UNIT, 40, 0, NAVIGATION, 0, 0, NULL},
  { "Settlers", SETTLER, UNIT, 40, 0, SURVIVAL, 0, 0, NULL},
  { "Submarine", SUBMARINE, UNIT, 50, 0, MASSPRODUCTION, 0, 0, NULL},
  { "Transport", TRANSPORT, UNIT, 50, 0, INDUSTRIALIZATION, 0, 0, NULL},
  { "Trireme", TRIREME, UNIT, 40, 0, MAPMAKING, 0, 0, NULL},

  { "Bank", BANK, BUILDING, 120, 3, BANKING, 0, 0, NULL},
  { "Temple", TEMPLE, BUILDING, 40, 1, CEREMONIALBURIAL, 0, 0, NULL},
  { "Courthouse", COURTHOUSE, BUILDING, 80, 1, CODEOFLAWS, 0, 0, NULL},
  { "Aquaduct", AQUADUCT, BUILDING, 120, 2, CONSTRUCTION, 0, 0, NULL},
  { "Colosseum", COLOSSEUM, BUILDING, 100, 3, CONSTRUCTION, 0, 0, NULL},
  { "Marketplace", MARKETPLACE, BUILDING, 80, 1, CURRENCY, 0, 0, NULL},
  { "Hydro Plant", HYDROPLANT, BUILDING, 240, 4, ELECTRONICS, 0, 0, NULL},
  { "Factory", FACTORY, BUILDING, 200, 4, INDUSTRIALIZATION, 0, 0, NULL},
  { "Palace", PALACE, BUILDING, 100, 0, MASONRY, 0, 0, NULL},
  // whats the palace supposed to cost? It needs to be supported by the first city right?
  { "City Walls", CITYWALLS, BUILDING, 120, 2, MASONRY, 0, 0, NULL},
  { "Mass Transit", MASSTRANSIT, BUILDING, 160, 4, MASSPRODUCTION, 0, 0, NULL},
  { "Granary", GRANARY, BUILDING, 60, 1, POTTERY, 0, 0, NULL},
  { "Recycling Ctr", RECYCLINGCTR, BUILDING, 200, 2, RECYCLING, 0, 0, NULL},
  { "Power Plant", POWERPLANT, BUILDING, 160, 4, REFINING, 0, 0, NULL},
  { "Fusion Plant", FUSIONPLANT, BUILDING, 160, 4, FUSIONPOWER, 0, 0, NULL},
  { "Nuclear Plant", NUCLEARPLANT, BUILDING, 200, 4, NUCLEARPOWER, 0, 0, NULL},
  { "Cathedral", CATHEDRAL, BUILDING, 160, 3, RELIGION, 0, 0, NULL},
  { "Mfg. Plant", MFGPLANT, BUILDING, 320, 6, ROBOTICS, 0, 0, NULL},
  { "SDI Defense", SDIDEFENSE, BUILDING, 200, 4, SUPERCONDUCTOR, 0, 0, NULL},
  { "University", UNIVERSITY, BUILDING, 160, 3, UNIVERSITIES, 0, 0, NULL},
  { "Library", LIBRARY, BUILDING, 80, 1, WRITING, 0, 0, NULL},
  { "Barracks", BARRACKS, BUILDING, 40, 1, SURVIVAL, 0, 0, NULL}, // this is a guess

  { "Copernicus Observatory", COPERNICUS, WONDER, 300, 0, ASTRONOMY, 0, 0, NULL},
  { "Hanging Gardens", HANGINGGARDENS, WONDER, 300, 0, POTTERY, 0, 0, NULL},
  { "Darwin's Voyage", DARWIN, WONDER, 300, 0, RAILROAD, 0, 0, NULL},
  { "Michelangelo's Chapel", MICHELANGELO, WONDER, 300, 0, RELIGION, 0, 0, NULL},
  { "J.S. Bach's Cathedral", JSBACH, WONDER, 400, 0, RELIGION, 0, 0, NULL},
  { "Isaac Newton's College", ISAACNEWTON, WONDER, 400, 0, THEORYOFGRAVITY, 0, 0, NULL},
  { "Pyramids", PYRAMIDS, WONDER, 300, 0, MASONRY, 0, 0, NULL},
  { "Great Wall", GREATWALL, WONDER, 300, 0, MASONRY, 0, 0, NULL},
  { "Shakespeare's Theatre", SHAKESPEARE, WONDER, 400, 0, MEDICINE, 0, 0, NULL},
  { "Oracle", ORACLE, WONDER, 300, 0, MYSTICISM, 0, 0, NULL},
  { "Magellan's Expedition", MAGELLAN, WONDER, 400, 0, NAVIGATION, 0, 0, NULL},
  { "Manhattan Project", MANHATTAN, WONDER, 600, 0, NUCLEARFISSION, 0, 0, NULL},
  { "Colossus", COLOSSUS, WONDER, 200, 0, BRONZEWORKING, 0, 0, NULL},
  { "United Nations", UNITEDNATIONS, WONDER, 600, 0, COMMUNISM, 0, 0, NULL},
  { "SETI Program", SETIPROGRAM, WONDER, 600, 0, COMPUTERS, 0, 0, NULL},
  { "Hoover Dam", HOOVERDAM, WONDER, 600, 0, ELECTRONICS, 0, 0, NULL},
  { "Cure for Cancer", CUREFORCANCER, WONDER, 600, 0, GENETICENGINEERING, 0, 0, NULL},
  { "Women's Suffrage", WOMENSSUFFRAGE, WONDER, 600, 0, INDUSTRIALIZATION, 0, 0, NULL},
  { "Great Library", GREATLIBRARY, WONDER, 300, 0, LITERACY, 0, 0, NULL},
  { "Light House", LIGHTHOUSE, WONDER, 200, 0, MAPMAKING, 0, 0, NULL},

  { "Nothing", NOOBJECT, 0, 0, 0, NOSCIENCE, 0, 0, NULL}
};

// really should read and re-order
UnitData unit_list[] = {
  {ARMOR, 10, 5, 3, &armor_pic},
  {ARTILLERY, 12, 2, 2, &artillery_pic},
  {BATTLESHIP, 18, 12, 4, &battleship_pic},
  {BOMBER, 12, 1, 8, &bomber_pic},
  {CANNON, 8, 1, 1, &cannon_pic},
  {CARAVAN, 0, 1, 1, &caravan_pic},
  {CARRIER, 1, 12, 5, &carrier_pic},
  {CATAPULT, 6, 1, 1, &catapult_pic},
  {CAVALRY, 2, 1, 2, &cavalry_pic},
  {CHARIOT,4, 1, 2, &chariot_pic},
  {CRUISER, 6, 6, 6, &cruiser_pic},
  {DIPLOMAT,0, 0, 2, &diplomat_pic},
  {FIGHTER, 4, 2, 10, &fighter_pic},
  {FRIGATE, 2, 2, 3, &frigate_pic},
  {IRONCLAD, 4, 4, 4, &ironclad_pic},
  {KNIGHTS, 4, 2, 2, &knights_pic},
  {LEGION, 3, 1, 1, &legion_pic},
  {MECH_INF, 6, 6, 3, &mechinf_pic},
  {MILITIA, 1, 1, 1, &militia_pic},
  {MUSKETEERS, 2, 3, 1, &musketeers_pic},
  {NUCLEAR, 99, 0, 16, &nuclear_pic},
  {PHALANX, 1, 2, 1, &phalanx_pic},
  {RIFLEMEN, 3, 5, 1, &riflemen_pic},
  {SAIL, 1, 1, 3, &sail_pic},
  {SETTLER, 0, 1, 1, &settler_pic},
  {SUBMARINE, 8, 2, 3, &submarine_pic},
  {TRANSPORT, 0, 3, 4, &transport_pic},
  {TRIREME, 1, 0, 3,  &trireme_pic},
  {NOOBJECT, 0, 0, 0, NULL}
};

struct obsolete_item
{
  Science science;
  BuildObject object;
};

obsolete_item obsolete_list[] = {
  {BRONZEWORKING, MILITIA},
  {METALLURGY, CATAPULT},
  {AUTOMOBILE, CANNON},
  {CHIVALRY, CHARIOT},
  {NAVIGATION, TRIREME},
  {MAGNETISM, SAIL},
  {INDUSTRIALIZATION, FRIGATE},
  {COMBUSTION, IRONCLAD},
  {GUNPOWDER, PHALANX},
  {CONSCRIPTION, MUSKETEERS},
  {COMMUNISM, PYRAMIDS},
  {INVENTION, HANGINGGARDENS},
  {ELECTRICITY, COLOSSUS},
  {MAGNETISM, LIGHTHOUSE},
  {UNIVERSITIES, GREATLIBRARY},
  {RELIGION, ORACLE},
  {GUNPOWDER, GREATWALL},
  {COMMUNISM, MICHELANGELO},
  {AUTOMOBILE, COPERNICUS},
  {ELECTRONICS, SHAKESPEARE},
  {NUCLEARFISSION, ISAACNEWTON},
  {NOSCIENCE, NOOBJECT}
};

void
SetupUnits(void)
{
  int i = 0;
  UnitData *unit;

  while(unit_list[i].type != NOOBJECT) {
    unit = &(Units[unit_list[i].type]);
    Units[unit_list[i].type] = unit_list[i];
    i++;
  }
}

void
SetupObjects(void)
{
  int i = 0;
  ObjectData *obj;

  for (i = 0; object_list[i].type != NOOBJECT; i++) { 
    obj = &(Objects[object_list[i].type]);
    *obj = object_list[i];
    buildObjects.Insert(&obj->type,StrKey(obj->name));
    if (i >= MAXOBJECTS || Objects[obj->type].type >= MAXOBJECTS)
      abort();
    obj->line = new char[60];
    
    // must match with selection handling  in player.cc
    if (obj->IsUnit())
      sprintf(obj->line, "%s %d/%d/%d  %d",obj->name, UnitAttack(obj->type),
	      UnitDefense(obj->type), UnitMoves(obj->type), obj->prodCost);
    else 
      sprintf(obj->line, "%s %d",obj->name, obj->prodCost);
  }
  Objects[NOOBJECT] = object_list[i];
  buildObjects.Insert(&object_list[i].type,StrKey(object_list[i].name));
}

HashTable<Science, StrKey> sciences(201);
HashTable<BuildObject, StrKey> buildObjects(201);

void
SetupSciences(void)
{
  int i;

  for (i = 0; science_list[i].type != NOSCIENCE; i++) {
    Sciences[science_list[i].type] = science_list[i];
    sciences.Insert(&science_list[i].type,StrKey(science_list[i].name));
    if (i >= FUTURETECH || science_list[i].type >= FUTURETECH)
      abort();
  }
  Sciences[science_list[i].type] = science_list[i];
  sciences.Insert(&science_list[i].type,StrKey(science_list[i].name));
}

void CheckRules()
{
  // FIXME: make sure the arrays are full
}

void InitializeRules()
{
  SetupUnits();
  SetupObjects();
  SetupSciences();
  CheckRules();
}

void CanDiscover(Player *player, List<Science> *canDiscover)
{
  canDiscover->Delete();

  Science i;
  FOR_ALL_SCIENCES(i) {
    Science sc = i;
    if (player->HasDiscovered(sc))
      continue;
    if (player->HasDiscovered(Sciences[sc].NeededScience[0]) &&
	player->HasDiscovered(Sciences[sc].NeededScience[1]))
      canDiscover->Insert(sc);
  }
  if (canDiscover->Count() == 0)
    canDiscover->Insert(FUTURETECH);
}

void Discover(Player *player, Science science)
{
  Debug('r', "Discovering %s for %d\n", science, player->id);
  
  switch(science) {
  case SURVIVAL:
    return;
    break;
  default:
    break;
  }
  
  for (int i = 0;obsolete_list[i].science != NOSCIENCE;i++)
    if (obsolete_list[i].science == science) {
      if (Objects[obsolete_list[i].object].IsWonder()) {
	if (!Objects[obsolete_list[i].object].isObsolete) {
	  Objects[obsolete_list[i].object].isObsolete = 1;
	  AddMessage(players[playerId]->messages, "%s makes %s obsolete",
		     Sciences[science].name, Objects[obsolete_list[i].object].name);
	}
      }
      else if (playerId == player->id) { // unit obsolete
	Objects[obsolete_list[i].object].isObsolete = 1;
	AddMessage(players[playerId]->messages, "%s makes %s obsolete",
		Sciences[science].name, Objects[obsolete_list[i].object].name);
      }
    }
}

void CanGovern(Player *player,List<charp> *canGovern)
{
  int pyramids = player->HasWonder(PYRAMIDS) && !Objects[PYRAMIDS].isObsolete;
  canGovern->Delete();

  canGovern->Insert("Despotism");
  if (player->HasDiscovered(MONARCHY) || pyramids)
    canGovern->Insert("Monarchy");
  if (player->HasDiscovered(THEREPUBLIC) || pyramids)
    canGovern->Insert("The Republic");
  if (player->HasDiscovered(DEMOCRACY))
    canGovern->Insert("Democracy");
}

void InitialScience(Player *player)
{
  player->Discover(SURVIVAL);
}

Science ObsoletedBy(BuildObject obj)
{
  for (int i = 0;obsolete_list[i].object != NOOBJECT;i++)
    if (obsolete_list[i].object == obj)
      return obsolete_list[i].science;
  return NOSCIENCE;
}

// MarkM added variable discovery costs for sciences:
// basically this was simply the recursive number of required sciences x15
// however that was too fast so now trying 15 x (1+number already have)
int DiscoverCost(Player *player, Science /*ptr*/)
{
  return player->discovered.Size() * 15;
}

void SaveRules()
{
  BuildObject b;
  FOR_ALL_BUILDOBJS(b)
    if (Objects[b].isObsolete)
      WriteUShort(b);
  WriteUShort(NOOBJECT);

  FOR_ALL_BUILDOBJS(b)
    if (Objects[b].built)
      WriteUShort(b);
  
  WriteUShort(NOOBJECT);
}

void RestoreRules()
{
  BuildObject b;

  while ((b = BuildObject(ReadUShort())) != NOOBJECT)
    Objects[b].isObsolete = 1;

  while ((b = BuildObject(ReadUShort())) != NOOBJECT)
    Objects[b].built = 1;
}
