#include "stdafx.h"
#include "../../SparAIContainer.h"
#include "../PerceptualState.h"
#include "CurrentWorld.h"

planning::PrimitiveType* CurrentWorld::UnitPrimitiveType;
planning::PrimitiveType* CurrentWorld::BuildingPrimitiveType;
planning::PrimitiveType* CurrentWorld::TechPrimitiveType;
planning::PrimitiveType* CurrentWorld::TechTypePrimitiveType;
planning::PrimitiveType* CurrentWorld::UpgradeTypePrimitiveType;
CurrentWorld::TechAcquiredAtomicFormula* CurrentWorld::TechAcquired;
CurrentWorld::NrUnitCompletedNumericFluent* CurrentWorld::NrUnitCompleted;

BuildingTypeObject* CurrentWorld::Nexus;
BuildingTypeObject* CurrentWorld::Pylon;
BuildingTypeObject* CurrentWorld::Assimilator;
BuildingTypeObject* CurrentWorld::Gateway;
BuildingTypeObject* CurrentWorld::Forge;
BuildingTypeObject* CurrentWorld::CyberneticsCore;
BuildingTypeObject* CurrentWorld::RoboticsFacility;
BuildingTypeObject* CurrentWorld::RoboticsSupportBay;
BuildingTypeObject* CurrentWorld::Observatory;
BuildingTypeObject* CurrentWorld::Stargate;
BuildingTypeObject* CurrentWorld::FleetBeacon;
BuildingTypeObject* CurrentWorld::ArbiterTribunal;
BuildingTypeObject* CurrentWorld::CitadelOfAdun;
BuildingTypeObject* CurrentWorld::TemplarArchives;
BuildingTypeObject* CurrentWorld::ShieldBattery;
BuildingTypeObject* CurrentWorld::PhotonCannon;

UnitTypeObject* CurrentWorld::Probe;
UnitTypeObject* CurrentWorld::Zealot;
UnitTypeObject* CurrentWorld::Dragoon;
UnitTypeObject* CurrentWorld::HighTemplar;
UnitTypeObject* CurrentWorld::DarkTemplar;
UnitTypeObject* CurrentWorld::Archon;
UnitTypeObject* CurrentWorld::DarkArchon;
UnitTypeObject* CurrentWorld::Reaver;
UnitTypeObject* CurrentWorld::Shuttle;
UnitTypeObject* CurrentWorld::Observer;
UnitTypeObject* CurrentWorld::Corsair;
UnitTypeObject* CurrentWorld::Scout;
UnitTypeObject* CurrentWorld::Carrier;
UnitTypeObject* CurrentWorld::Arbiter;

TechTypeObject* CurrentWorld::DisruptionWeb;
TechTypeObject* CurrentWorld::Feedback;
TechTypeObject* CurrentWorld::Hallucination;
TechTypeObject* CurrentWorld::Maelstrom;
TechTypeObject* CurrentWorld::MindControl;
TechTypeObject* CurrentWorld::PsionicStorm;
TechTypeObject* CurrentWorld::Recall;
TechTypeObject* CurrentWorld::StasisField;

UpgradeTypeObject* CurrentWorld::GroundWeapons[3];
UpgradeTypeObject* CurrentWorld::GroundArmor[3];
UpgradeTypeObject* CurrentWorld::AirWeapons[3];
UpgradeTypeObject* CurrentWorld::AirArmor[3];
UpgradeTypeObject* CurrentWorld::PlasmaShields[3];
UpgradeTypeObject* CurrentWorld::SingularityCharge;
UpgradeTypeObject* CurrentWorld::LegEnhancements;
UpgradeTypeObject* CurrentWorld::GraviticDrive;
UpgradeTypeObject* CurrentWorld::CarrierCapacity;
UpgradeTypeObject* CurrentWorld::ReaverCapacity;
UpgradeTypeObject* CurrentWorld::ScarabDamage;
UpgradeTypeObject* CurrentWorld::GraviticBoosters;
UpgradeTypeObject* CurrentWorld::SensorArray;
UpgradeTypeObject* CurrentWorld::ApialSensors;
UpgradeTypeObject* CurrentWorld::GraviticThrusters;
UpgradeTypeObject* CurrentWorld::ArgusJewel;
UpgradeTypeObject* CurrentWorld::ArgusTalisman;
UpgradeTypeObject* CurrentWorld::KhaydarinAmulet;
UpgradeTypeObject* CurrentWorld::KhaydarinCore;

CurrentWorld::TechAcquiredAtomicFormula::TechAcquiredAtomicFormula()
  : CurrentWorldAtomicFormula("TechAcquired", new planning::ObjectParameterVariable("tech", CurrentWorld::TechPrimitiveType), planning::DescribedFormula::Attributes(false))
{
}

CurrentWorld::NrUnitCompletedNumericFluent::NrUnitCompletedNumericFluent()
  : CurrentWorldNumericFluent("NrUnitCompleted", new planning::ObjectParameterVariable("unit-type", CurrentWorld::UnitPrimitiveType), planning::NumericFluent::Attributes(false))
{
}

CurrentWorld::CurrentWorld()
  : m_techAcquiredEvent(*this)
  , m_unitTypeCompletedChangedEvent(*this)
  , m_unitTypeCreatableChangedEvent(*this)
  , m_onUnitCompletedEventHandler(*this)
  , m_onUnitDestroyEventHandler(*this)
  , m_onUnitMorphEventHandler(*this)
  , m_onUnitRenegadeEventHandler(*this)
{
}

void CurrentWorld::initialize()
{
  s_currentWorld.m_onUnitCompletedID = Spar->getPerceptualState()->getUnitCompletedEvent().subscribe(&s_currentWorld.m_onUnitCompletedEventHandler, NULL);
  s_currentWorld.m_onUnitDestroyID = BWAPI::getUnitDestroyEvent().subscribe(&s_currentWorld.m_onUnitDestroyEventHandler, NULL);
  s_currentWorld.m_onUnitMorphID = BWAPI::getUnitMorphEvent().subscribe(&s_currentWorld.m_onUnitMorphEventHandler, NULL);
  s_currentWorld.m_onUnitRenegadeID = BWAPI::getUnitRenegadeEvent().subscribe(&s_currentWorld.m_onUnitRenegadeEventHandler, NULL);

  UnitPrimitiveType = new planning::PrimitiveType("UnitType", planning::PrimitiveType::getObjectType());
  UnitPrimitiveType->acquire(&s_currentWorld);
  BuildingPrimitiveType = new planning::PrimitiveType("BuildingType", UnitPrimitiveType);
  BuildingPrimitiveType->acquire(&s_currentWorld);
  TechPrimitiveType = new planning::PrimitiveType("TechType", planning::PrimitiveType::getObjectType());
  TechPrimitiveType->acquire(&s_currentWorld);
  TechTypePrimitiveType = new planning::PrimitiveType("TechTypeType", TechPrimitiveType);
  TechTypePrimitiveType->acquire(&s_currentWorld);
  UpgradeTypePrimitiveType = new planning::PrimitiveType("UpgradeType", TechPrimitiveType);
  UpgradeTypePrimitiveType->acquire(&s_currentWorld);
  TechAcquired = new TechAcquiredAtomicFormula();
  TechAcquired->acquire(&s_currentWorld);
  NrUnitCompleted = new NrUnitCompletedNumericFluent();
  NrUnitCompleted->acquire(&s_currentWorld);

  Nexus = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Nexus);
  Pylon = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Pylon);
  Assimilator = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Assimilator);
  Gateway = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Gateway);
  ShieldBattery = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Shield_Battery);
  Forge = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Forge);
  PhotonCannon = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Photon_Cannon);
  CyberneticsCore = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Cybernetics_Core);
  RoboticsFacility = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Robotics_Facility);
  RoboticsSupportBay = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Robotics_Support_Bay);
  Observatory = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Observatory);
  Stargate = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Stargate);
  FleetBeacon = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Fleet_Beacon);
  CitadelOfAdun = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Citadel_of_Adun);
  TemplarArchives = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Templar_Archives);
  ArbiterTribunal = new BuildingTypeObject(BWAPI::UnitTypes::Protoss_Arbiter_Tribunal);

  Probe = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Probe, UnitPrimitiveType);
  Zealot = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Zealot, UnitPrimitiveType);
  Dragoon = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Dragoon, UnitPrimitiveType);
  HighTemplar = new UnitTypeObject(BWAPI::UnitTypes::Protoss_High_Templar, UnitPrimitiveType);
  DarkTemplar = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Dark_Templar, UnitPrimitiveType);
  Archon = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Archon, UnitPrimitiveType);
  DarkArchon = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Dark_Archon, UnitPrimitiveType);
  Shuttle = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Shuttle, UnitPrimitiveType);
  Reaver = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Reaver, UnitPrimitiveType);
  Observer = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Observer, UnitPrimitiveType);
  Corsair = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Corsair, UnitPrimitiveType);
  Scout = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Scout, UnitPrimitiveType);
  Carrier = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Carrier, UnitPrimitiveType);
  Arbiter = new UnitTypeObject(BWAPI::UnitTypes::Protoss_Arbiter, UnitPrimitiveType);

  DisruptionWeb = new TechTypeObject(BWAPI::TechTypes::Disruption_Web);
  Feedback = new TechTypeObject(BWAPI::TechTypes::Feedback);
  Hallucination = new TechTypeObject(BWAPI::TechTypes::Hallucination);
  Maelstrom = new TechTypeObject(BWAPI::TechTypes::Maelstrom);
  MindControl = new TechTypeObject(BWAPI::TechTypes::Mind_Control);
  PsionicStorm = new TechTypeObject(BWAPI::TechTypes::Psionic_Storm);
  Recall = new TechTypeObject(BWAPI::TechTypes::Recall);
  StasisField = new TechTypeObject(BWAPI::TechTypes::Stasis_Field);

  GroundWeapons[0] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Weapons, 1);
  GroundWeapons[1] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Weapons, 2);
  GroundWeapons[2] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Weapons, 3);
  GroundArmor[0] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Armor, 1);
  GroundArmor[1] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Armor, 2);
  GroundArmor[2] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Ground_Armor, 3);
  AirWeapons[0] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Weapons, 1);
  AirWeapons[1] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Weapons, 2);
  AirWeapons[2] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Weapons, 3);
  AirArmor[0] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Armor, 1);
  AirArmor[1] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Armor, 2);
  AirArmor[2] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Air_Armor, 3);
  PlasmaShields[0] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Plasma_Shields, 1);
  PlasmaShields[1] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Plasma_Shields, 2);
  PlasmaShields[2] = new UpgradeTypeObject(BWAPI::UpgradeTypes::Protoss_Plasma_Shields, 3);
  SingularityCharge = new UpgradeTypeObject(BWAPI::UpgradeTypes::Singularity_Charge);
  LegEnhancements = new UpgradeTypeObject(BWAPI::UpgradeTypes::Leg_Enhancements);
  GraviticDrive = new UpgradeTypeObject(BWAPI::UpgradeTypes::Gravitic_Drive);
  CarrierCapacity = new UpgradeTypeObject(BWAPI::UpgradeTypes::Carrier_Capacity);
  ReaverCapacity = new UpgradeTypeObject(BWAPI::UpgradeTypes::Reaver_Capacity);
  ScarabDamage = new UpgradeTypeObject(BWAPI::UpgradeTypes::Scarab_Damage);
  GraviticBoosters = new UpgradeTypeObject(BWAPI::UpgradeTypes::Gravitic_Boosters);
  SensorArray = new UpgradeTypeObject(BWAPI::UpgradeTypes::Sensor_Array);
  ApialSensors = new UpgradeTypeObject(BWAPI::UpgradeTypes::Apial_Sensors);
  GraviticThrusters = new UpgradeTypeObject(BWAPI::UpgradeTypes::Gravitic_Thrusters);
  ArgusJewel = new UpgradeTypeObject(BWAPI::UpgradeTypes::Argus_Jewel);
  ArgusTalisman = new UpgradeTypeObject(BWAPI::UpgradeTypes::Argus_Talisman);
  KhaydarinAmulet = new UpgradeTypeObject(BWAPI::UpgradeTypes::Khaydarin_Amulet);
  KhaydarinCore = new UpgradeTypeObject(BWAPI::UpgradeTypes::Khaydarin_Core);

  for (stdext::hash_map<BWAPI::UnitType, BuildingTypeObject*>::iterator it = s_currentWorld.m_buildingTypeObjects.begin();
       it != s_currentWorld.m_buildingTypeObjects.end();
       ++it)
    it->second->acquire(&s_currentWorld);
  for (stdext::hash_map<BWAPI::UnitType, UnitTypeObject*>::iterator it = s_currentWorld.m_unitTypeObjects.begin();
       it != s_currentWorld.m_unitTypeObjects.end();
       ++it)
    it->second->acquire(&s_currentWorld);
  for (stdext::hash_map<BWAPI::TechType, TechTypeObject*>::iterator it = s_currentWorld.m_techTypeObjects.begin();
       it != s_currentWorld.m_techTypeObjects.end();
       ++it)
    it->second->acquire(&s_currentWorld);
  for (stdext::hash_map<std::pair<BWAPI::UpgradeType, int>, UpgradeTypeObject*>::iterator it = s_currentWorld.m_upgradeTypeObjects.begin();
       it != s_currentWorld.m_upgradeTypeObjects.end();
       ++it)
    it->second->acquire(&s_currentWorld);

  // No need for that since BuildingTypeObjects are UnitTypeObjects
  //for (stdext::hash_map<BWAPI::UnitType, BuildingTypeObject*>::iterator it = s_currentWorld.m_buildingTypeObjects.begin();
  //     it != s_currentWorld.m_buildingTypeObjects.end();
  //     ++it)
  //  it->second->initialize();
  for (stdext::hash_map<BWAPI::UnitType, UnitTypeObject*>::iterator it = s_currentWorld.m_unitTypeObjects.begin();
       it != s_currentWorld.m_unitTypeObjects.end();
       ++it)
    it->second->initialize();
  for (stdext::hash_map<BWAPI::TechType, TechTypeObject*>::iterator it = s_currentWorld.m_techTypeObjects.begin();
       it != s_currentWorld.m_techTypeObjects.end();
       ++it)
    it->second->initialize();
  for (stdext::hash_map<std::pair<BWAPI::UpgradeType, int>, UpgradeTypeObject*>::iterator it = s_currentWorld.m_upgradeTypeObjects.begin();
       it != s_currentWorld.m_upgradeTypeObjects.end();
       ++it)
    it->second->initialize();
}

void CurrentWorld::terminate()
{
  Spar->getPerceptualState()->getUnitCompletedEvent().unsubscribe(s_currentWorld.m_onUnitCompletedID);
  BWAPI::getUnitDestroyEvent().unsubscribe(s_currentWorld.m_onUnitDestroyID);
  BWAPI::getUnitMorphEvent().unsubscribe(s_currentWorld.m_onUnitMorphID);
  BWAPI::getUnitRenegadeEvent().unsubscribe(s_currentWorld.m_onUnitRenegadeID);

  for (stdext::hash_map<BWAPI::UnitType, BuildingTypeObject*>::iterator it = s_currentWorld.m_buildingTypeObjects.begin();
       it != s_currentWorld.m_buildingTypeObjects.end();
       ++it)
  {
    it->second->releaseMembers();
    it->second->release(&s_currentWorld);
  }
  for (stdext::hash_map<BWAPI::UnitType, UnitTypeObject*>::iterator it = s_currentWorld.m_unitTypeObjects.begin();
       it != s_currentWorld.m_unitTypeObjects.end();
       ++it)
  {
    it->second->releaseMembers();
    it->second->release(&s_currentWorld);
  }
  for (stdext::hash_map<BWAPI::TechType, TechTypeObject*>::iterator it =s_currentWorld. m_techTypeObjects.begin();
       it != s_currentWorld.m_techTypeObjects.end();
       ++it)
  {
    it->second->releaseMembers();
    it->second->release(&s_currentWorld);
  }
  for (stdext::hash_map<std::pair<BWAPI::UpgradeType, int>, UpgradeTypeObject*>::iterator it = s_currentWorld.m_upgradeTypeObjects.begin();
       it != s_currentWorld.m_upgradeTypeObjects.end();
       ++it)
  {
    it->second->releaseMembers();
    it->second->release(&s_currentWorld);
  }

  UnitPrimitiveType->release(&s_currentWorld);
  BuildingPrimitiveType->release(&s_currentWorld);
  TechPrimitiveType->release(&s_currentWorld);
  TechTypePrimitiveType->release(&s_currentWorld);
  UpgradeTypePrimitiveType->release(&s_currentWorld);

  TechAcquired->releaseMembers();
  TechAcquired->release(&s_currentWorld);

  NrUnitCompleted->releaseMembers();
  NrUnitCompleted->release(&s_currentWorld);
}

void CurrentWorld::_output(std::ostream& out) const
{
  out << "Current world";
}

#pragma region IClosedWorld implementation

bool CurrentWorld::isSet(const planning::AtomicFormulaApplication* formula) const
{
  return dynamic_cast<const CurrentWorldAtomicFormula*>(formula->getAtomicFormula())->evaluate(formula);
}

double CurrentWorld::getNumericFluent(const planning::NumericFluentApplication* fluent) const
{
  return dynamic_cast<const CurrentWorldNumericFluent*>(fluent->getNumericFluent())->evaluate(fluent);
}

bool CurrentWorld::TechAcquiredAtomicFormula::evaluate(const planning::AtomicFormulaApplication* formula) const
{
  return static_cast<const TechObject*>(formula->getArgument(0))->isAcquired();
}

double CurrentWorld::NrUnitCompletedNumericFluent::evaluate(const planning::NumericFluentApplication* fluent) const
{
  const UnitTypeObject* object = static_cast<const UnitTypeObject*>(fluent->getArgument(0));

  return Spar->getPerceptualState()->getMap()->getNbCompletedOwnUnits(object->getUnitType());
}

#pragma endregion

#pragma region TODO-PLANNING-DELETE

void CurrentWorld::onUnitCompleted(void*, BWAPI::Unit* unit)
{
  if (unit->getPlayer() == BWAPI::Broodwar->self())
  {
    UnitTypeObject* tech = CurrentWorld::getUnitTypeObject(unit->getType());
    if (tech != NULL)
    {
      tech->update();
    }
  }
}

void CurrentWorld::onUnitDestroy(void*, BWAPI::Unit* unit)
{
  if (unit->getPlayer() == BWAPI::Broodwar->self())
  {
    UnitTypeObject* tech = CurrentWorld::getUnitTypeObject(unit->getType());
    if (tech != NULL)
    {
      tech->update();
    }
  }
}

void CurrentWorld::onUnitMorph(void*, BWAPI::Unit* unit, BWAPI::UnitType previousType)
{
  UnitTypeObject* tech1 = CurrentWorld::getUnitTypeObject(previousType);
  if (tech1 != NULL)
  {
    tech1->update();
  }

  UnitTypeObject* tech2 = CurrentWorld::getUnitTypeObject(unit->getType());
  if (tech2 != NULL)
  {
    tech2->update();
  }
}

void CurrentWorld::onUnitRenegade(void*, BWAPI::Unit* unit, BWAPI::Player* previousPlayer)
{
  if (previousPlayer == BWAPI::Broodwar->self())
  {
    UnitTypeObject* tech = CurrentWorld::getUnitTypeObject(unit->getType());
    if (tech != NULL)
    {
      tech->update();
    }
  }
}

#pragma endregion