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

struct ObjectList
{
  char *name;
  int type;
  int unit;
  int prodCost;
  int maint;
};

typedef List<charp> Strings;

char *science_list[] = {
  "Advanced Flight", "Flight", "Electricity", NULL,
  "Bomber", "Carrier", NULL,

  "Alphabet", NULL,
  NULL,

  "Astronomy", "Mysticism", "Mathematics", NULL,
  "Copernicus Observatory", NULL,

  "Atomic Theory", "Theory of Gravity", "Physics", NULL,
  NULL,

  "Automobile", "Combustion", "Steel", NULL,
  "Armor", NULL,

  "Banking", "Trade", "The Republic", NULL,
  "Bank", NULL,

  "Bridge Building", "Iron Working", "Construction", NULL,
  NULL,

  "Bronze Working", NULL,
  "Phalanx", "Colossus", NULL,

  "Ceremonial Burial", NULL,
  "Temple", NULL,

  "Chemistry", "University", "Medicine", NULL,
  NULL,

  "Chivalry", "Feudalism", "Horseback Riding", NULL,
  "Knights", NULL,

  "Code of Laws", "Alphabet", NULL,
  "Courthouse", NULL,

  "Combustion", "Refining", "Explosives", NULL,
  "Cruiser", NULL,

  "Communism", "Philosophy", "Industrialization", NULL,
  "United Nations", NULL,

  "Computers", "Mathematics", "Electricity", NULL,
  "SETI Program", NULL,

  "Conscription", "The Republic", "Explosives", NULL,
  "Riflemen", NULL,

  "Construction", "Masonry", "Currency", NULL,
  "Aqueduct", "Colosseum", NULL,

  "Currency", "Bronze Working", NULL,
  "Marketplace", NULL,

  "Democracy", "Philosophy", "Literacy", NULL,
  NULL,

  "Electricity", "Metallurgy", "Magnetism", NULL,
  NULL,

  "Electronics", "Engineering", "Electricity", NULL,
  "Hydro Plant", "Hoover Dam", NULL,

  "Engineering", "The Wheel", "Construction", NULL,
  NULL,

  "Explosives", "Gunpowder", "Chemistry", NULL,
  NULL,

  "Feudalism", "Masonry", "Monarchy", NULL,
  NULL,

  "Flight", "Combustion", "Physics", NULL,
  "Fighter", NULL,

  "Fusion Power", "Nuclear Power", "Superconductor", NULL,
  NULL,

  "Genetic Engineering", "Medicine", "The Corporation", NULL,
  "Cure for Cancer", NULL,

  "Gunpowder", "Invention", "Iron Working", NULL,
  "Musketeers", NULL,

  "Horseback Riding", NULL,
  "Cavalry", NULL,

  "Industrialization", "Railroad", "Banking", NULL,
  "Transport", "Factory", "Women's Suffrage", NULL,

  "Invention", "Engineering", "Literacy", NULL,
  NULL,

  "Iron Working", "Bronze Working", NULL,
  "Legion", NULL,

  "Labor Union", "Mass Production", "Communism", NULL,
  "Mech. Infantry", NULL,

  "Literacy", "Writing", "Code of Laws", NULL,
  "Great Library", NULL,

  "Magnetism", "Navigation", "Physics", NULL,
  "Frigate", NULL,

  "Map Making", "Alphabet", NULL,
  "Trireme", "Light House", NULL,

  "Masonry", NULL,
  "Palace", "City Walls", "Pyramids", "Great Wall", NULL,

  "Mass Production", "Automobile", "The Corporation", NULL,
  "Submarine", "Mass Transit", NULL,

  "Mathematics", "Alphabet", "Masonry", NULL,
  "Catapult", NULL,

  "Medicine", "Philosophy", "Trade", NULL,
  "Shakespeare's Theatre", NULL,

  "Metallurgy", "Gunpowder", "University", NULL,
  "Cannon", NULL,

  "Monarchy", "Ceremonial Burial", "Code of Laws", NULL,
  NULL,

  "Mysticism", "Ceremonial Burial", NULL,
  "Oracle", NULL,

  "Navigation", "Map Making", "Astronomy", NULL,
  "Sail", "Magellen's Expedition", NULL,

  "Nuclear Fission", "Mass Production", "Atomic Theory", NULL,
  "Manhattan Project", NULL,

  "Nuclear Power", "Nuclear Fission", "Electronics", NULL,
  NULL,

  "Philosophy", "Mysticism", "Literacy", NULL,
  NULL,

  "Physics", "Mathematics", "Navigation", NULL,
  NULL,

  "Plastics", "Refining", NULL,
  NULL,

  "Pottery", NULL,
  "Granary", "Hanging Gardens", NULL,

  "Railroad", "Bridge Building", "Steam Engine", NULL,
  "Darwin's Voyage", NULL,

  "Recycling", "Mass Production", "Democracy", NULL,
  "Recycling Ctr", NULL,

  "Refining", "Chemistry", "The Corporation", NULL,
  "Power Plant", NULL,

  "Religion", "Philosophy", "Writing", NULL,
  "Cathedral", "Michelangelo's Chapel", "J.S. Bach's Cathedral", NULL,

  "Robotics", "Plastics", "Computers", NULL,
  "Artillery", "Mfg. Plant", NULL,

  "Rocketry", "Advanced Flight", "Electronics", "Manhattan Project", NULL,
  "Nuclear", NULL,

  "Steam Engine", "Physics", "Invention", NULL,
  "Ironclad", NULL,

  "Steel", "Metallurgy", "Industrialization", NULL,
  "Battleship", NULL,

  "Superconductor", "Plastics", "Mass Production", NULL,
  "SDI Defense", NULL,

  "Survival", NULL,
  "Settlers", "Militia", NULL,

  "The Corporation", "Banking", "Industrialization", NULL,
  NULL,

  "The Republic", "Code of Laws", "Literacy", NULL,
  NULL,

  "The Wheel", NULL,
  "Chariot", NULL,

  "Theory of Gravity", "Astronomy", "University", NULL,
  "Isaac Newton's College", NULL,

  "Trade", "Currency", "Code of Laws", NULL,
  "Caravan", NULL,

  "University", "Mathematics", "Philosophy", NULL,
  "University", NULL,

  "Writing", "Alphabet", NULL,
  "Diplomat", "Library", NULL,

  NULL
  };

ObjectList object_list[] = {
  { "Armor", ARMOR, 1, 80, 0 },
  { "Artillery", ARTILLERY, 1, 60, 0 },
  { "Battleship", BATTLESHIP, 1, 160, 0 },
  { "Bomber", BOMBER, 1, 120, 0 },
  { "Cannon", CANNON, 1, 40, 0 },
  { "Caravan", CARAVAN, 1, 50, 0 },
  { "Carrier", CARRIER, 1,160, 0 },
  { "Catapult", CATAPULT, 1, 40, 0 },
  { "Cavalry", CAVALRY, 1, 20, 0 },
  { "Chariot", CHARIOT, 1, 40, 0 },
  { "Cruiser", CRUISER, 1, 80, 0 },
  { "Diplomat", DIPLOMAT, 1, 30, 0 },
  { "Fighter", FIGHTER, 1, 60, 0 },
  { "Frigate", FRIGATE, 1, 40, 0 },
  { "Ironclad", IRONCLAD, 1, 60, 0 },
  { "Knights", KNIGHTS, 1, 40, 0 },
  { "Legion", LEGION, 1, 20, 0 },
  { "Mech. Infantry", MECH_INF, 1, 50, 0 },
  { "Militia", MILITIA, 1, 10, 0 },
  { "Musketeers", MUSKETEERS, 1, 30, 0 },
  { "Nuclear", NUCLEAR, 1, 160, 0 },
  { "Phalanx", PHALANX, 1, 20, 0 },
  { "Riflemen", RIFLEMEN, 1, 30, 0 },
  { "Sail", SAIL, 1, 40, 0 },
  { "Settlers", SETTLER, 1, 40, 0 },
  { "Submarine", SUBMARINE, 1, 50, 0 },
  { "Transport", TRANSPORT, 1, 50, 0 },
  { "Trireme", TRIREME, 1, 40, 0 },
  { "Copernicus Observatory", COPERNICUS, 2, 300, 0 },
  { "Bank", BANK, 0, 120, 3 },
  { "Colossus", COLOSSUS, 2, 200, 0 },
  { "Temple", TEMPLE, 0, 40, 1 },
  { "Courthouse", COURTHOUSE, 0, 80, 1 },
  { "United Nations", UNITEDNATIONS, 2, 600, 0 },
  { "SETI Program", SETIPROGRAM, 2, 600, 0 },
  { "Aqueduct", AQUEDUCT, 0, 120, 2 },
  { "Colosseum", COLOSSEUM, 0, 100, 3 },
  { "Marketplace", MARKETPLACE, 0, 80, 1 },
  { "Hydro Plant", HYDROPLANT, 0, 200, 4 },
  { "Hoover Dam", HOOVERDAM, 2, 600, 0 },
  { "Cure for Cancer", CUREFORCANCER, 2, 600, 0 },
  { "Factory", FACTORY, 0, 200, 4 },
  { "Women's Suffrage", WOMENSSUFFRAGE, 2, 600, 0 },
  { "Great Library", GREATLIBRARY, 2, 300, 0 },
  { "Light House", LIGHTHOUSE, 2, 200, 0 },
  { "Palace", PALACE, 0, 100, 4 },
  { "City Walls", CITYWALLS, 0, 120, 2 },
  { "Pyramids", PYRAMIDS, 2, 300, 0 },
  { "Great Wall", GREATWALL, 2, 300, 0 },
  { "Mass Transit", MASSTRANSIT, 0, 160, 4 },
  { "Shakespeare's Theatre", SHAKESPEARE, 2, 400, 0 },
  { "Oracle", ORACLE, 2, 300, 0 },
  { "Magellen's Expedition", MAGELLEN, 2, 400, 0 },
  { "Manhattan Project", MANHATTAN, 2, 600, 0 },
  { "Granary", GRANARY, 0, 60, 1 },
  { "Hanging Gardens", HANGINGGARDENS, 2, 300, 0 },
  { "Darwin's Voyage", DARWIN, 2, 300, 0 },
  { "Recycling Ctr", RECYCLINGCTR, 0, 200, 2 },
  { "Power Plant", POWERPLANT, 0, 160, 4 },
  { "Cathedral", CATHEDRAL, 0, 160, 3 },
  { "Michelangelo's Chapel", MICHELANGELO, 2, 300, 0 },
  { "J.S. Bach's Cathedral", JSBACH, 2, 400, 0 },
  { "Mfg. Plant", MFGPLANT, 0, 320, 6 },
  { "SDI Defense", SDIDEFENSE, 0, 200, 4 },
  { "Isaac Newton's College", ISAACNEWTON, 2, 400, 0 },
  { "University", UNIVERSITY, 0, 160, 3 },
  { "Library", LIBRARY, 0, 80, 1 },
  { NULL, 0, 0, 0, 0 }
  };

char *object_need_list[] = {
  "University", "Library", NULL,
  "Bank", "Marketplace", NULL,
  "Power Plant", "Factory", NULL,
  "Mfg. Plant", "Factory", NULL,
  "Hydro Plant", "Factory", NULL,
  NULL
  };

char *obselete_list[] = {
  "Phalanx", "Militia",
  "Cannon", "Catapult",
  "Armor", "Cannon",
  "Knights", "Chariot",
  "Sail", "Trireme",
  "Frigate", "Sail",
  "Transport", "Frigate",
  "Cruiser", "Ironclad",
  "Musketeers", "Phalanx",
  "Riflemen", "Musketeers",
  NULL };

char *wonder_obsolete_list[] = {
  "Pyramids", "Communism",
  "Hanging Gardens", "Invention",
  "Colossus", "Electricity",
  "Light House", "Magnetism",
  "Great Library", "University",
  "Oracle", "Religion",
  "Great Wall", "Gunpowder",
  "Michelangelo's Chapel", "Communism",
  "Copernicus Observatory", "Automobile",
  "Shakespeare's Theatre", "Electronics",
  "Isaac Newton's College", "Nuclear Fission",
  NULL
  };

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

// hashes on science, each object is a list of science names which
// need the key science
HashTable<Strings, StrKey> needTable(201);

List<charp> scienceNames;

char **NewScience(char **ptr)
{
  Science *sc = new Science;
  sc->name = *ptr++;
  sc->discovered = 0;
  sc->wonderObsolete = NULL;
  while (*ptr != NULL) {
    char *name = *ptr++;
    sc->need.Insert(name);

    Strings *strings = needTable.Find(StrKey(name));
    if (strings == NULL) {
      strings = new Strings;
      needTable.Insert(strings, StrKey(name));
    }
    strings->Insert(sc->name); // sc->name depends on name
  }
  ++ptr; // get past NULL

  while (*ptr != NULL)
    sc->canBuild.Insert(*ptr++);
  ++ptr; // get past NULL
  sciences.Insert(sc, StrKey(sc->name));
  scienceNames.Insert(sc->name);
  return ptr;
}

ObjectList *NewObject(ObjectList *objList)
{
  BuildObject *obj = new BuildObject;
  obj->name = objList->name;
  obj->type = objList->type;
  obj->unit = objList->unit == 1;
  obj->wonder = objList->unit == 2;
  obj->prodCost = objList->prodCost;
  obj->maint = objList->maint;
  obj->built = 0;
  obj->isObsolete = 0;
  obj->obsolete = NULL;
  buildObjects.Insert(obj, StrKey(obj->name));
  ++objList;
  return objList;
}

void CheckRules()
{
  // for each science, check that the thing it needs is in the science
  // table
  // also check the things it can build are in the objects table
  for (Lister<charp> lister = scienceNames; lister; lister.Next()) {
    char *name = lister.Elem();
    Science *sc = sciences.Find(StrKey(name));
    Lister<charp> l = sc->need;
    while (l) {
      if (sciences.Find(StrKey(l.Elem())) == NULL)
	Debug('r', "Science %s depends on non existant %s\n", name, l.Elem());
      l.Next();
    }
    l = sc->canBuild;
    while (l) {
      if (buildObjects.Find(StrKey(l.Elem())) == NULL)
	Debug('r', "Science %s can't build non existant %s\n", name, l.Elem());
      l.Next();
    }
  }
}

void InitializeRules()
{
  char **ptr = science_list;
  while (*ptr != NULL)
    ptr = NewScience(ptr);

  ObjectList *obj = object_list;
  while (obj->name != NULL)
    obj = NewObject(obj);

  ptr = object_need_list;
  while (*ptr != NULL) {
    BuildObject *obj = buildObjects.Find(StrKey(*ptr));
    if (obj == NULL)
      Debug('r', "Can't find object %s in InitializeRules\n", *ptr);
    while (*++ptr != NULL)
      obj->neededObj.Insert(*ptr);
    ++ptr;
  }
  ptr = obselete_list;
  while (*ptr != NULL) {
    BuildObject *obj = buildObjects.Find(StrKey(*ptr++));
    if (obj == NULL)
      Debug('r', "Can't find %s in Obselete\n", *(ptr-1));
    else
      obj->obsolete = *ptr;
    ++ptr;
  }

  ptr = wonder_obsolete_list;
  while (*ptr != NULL) {
    BuildObject *obj = buildObjects.Find(StrKey(*ptr++));
    if (obj == NULL)
      Debug('r', "Can't find %s in obsolete\n", *(ptr-1));
    Science *sc = sciences.Find(StrKey(*ptr++));
    if (sc == NULL)
      Debug('r', "Can't find %s in obsolete\n", *(ptr-1));
    if (sc != NULL)
      sc->wonderObsolete = obj->name;
  }

  CheckRules();
}

void CanDiscover(int player, char *science, List<charp> &canDiscover)
{
  Science *ptr = sciences.Find(StrKey(science));
  int can = 1;
  for (Lister<charp> needl = ptr->need; needl && can; needl.Next()) {
    Science *ptr1 = sciences.Find(needl.Elem());
    if (ptr1 != NULL)
      can = ptr1->discovered & (1<<player);
    else
      can = players[player]->HasWonder(needl.Elem());
  }
  if (can)
    canDiscover.Insert(ptr->name);
}

void Discover(int player, char *science, List<charp> &discovered,
	      List<charp> &canDiscover, List<charp> &canBuild)
{
  Debug('r', "Discovering %s for %d\n", science, player);
  Science *ptr = sciences.Find(StrKey(science));
  science = ptr->name;
  if (strcmp(science, "Mysticism") == 0)
    players[player]->templeEffect = 2;
  discovered.Insert(science);
  for (Lister<charp> lister = canDiscover; lister; lister.Next())
    if (strcmp(lister.Elem(), science) == 0) {
      lister.Delete();
      break;
    }
  ptr->discovered |= 1 << player;
  if (ptr->wonderObsolete != NULL) {
    BuildObject *wonder = buildObjects.Find(StrKey(ptr->wonderObsolete));
    if (!wonder->isObsolete) {
      wonder->isObsolete = 1;
      AddMessage(players[playerId]->messages, "%s makes %s obsolete",
		 science, ptr->wonderObsolete);
    }
  }

  Strings *strings = needTable.Find(StrKey(science));
  if (strings != NULL) {
    for (Lister<charp> stringl = *strings; stringl; stringl.Next()) {
      CanDiscover(player, stringl.Elem(), canDiscover);
    }
  }

  for (lister = ptr->canBuild; lister; lister.Next()) {
    char *name = lister.Elem();
    canBuild.Insert(name);
    BuildObject *obj = buildObjects.Find(StrKey(name));
    if (obj->obsolete != NULL) {
      for (Lister<charp> l = canBuild; l; l.Next())
	if (strcmp(l.Elem(), obj->obsolete) == 0) {
	  l.Delete();
	  break;
	}
    }
  }
}

void InitialScience(int player, List<charp> &discovered,
		    List<charp> &canDiscover, List<charp> &canBuild)
{
  Lister<charp> l = scienceNames;
  // look for all sciences with an empty need list put into canDiscover
  // and discover survival
  while (l) {
    Science *sc = sciences.Find(StrKey(l.Elem()));
    if (!sc->need)
      canDiscover.Insert(sc->name);
    l.Next();
  }
  Discover(player, "Survival", discovered, canDiscover, canBuild);
}

int Discovered(int player, char *science)
{
  Science *sc = sciences.Find(StrKey(science));
  if (sc == NULL) return 0;
  return sc->discovered & (1<<player);
}

int WonderObsolete(BuildObject *wonder, int player)
{
  return wonder->isObsolete;
}
