#include "stdafx.h"
#include "SparObject.h"
#include "CurrentWorld.h"

SparObject::SparObject(const std::string& name, planning::PrimitiveType* type)
  : planning::Object(name, type)
{
}

void SparObject::releaseMembers()
{
}

void SparObject::_output(std::ostream& out) const
{
  out << m_name;
}

#pragma region TODO-PLANNING-DELETE

#include "TechTypeObject.h"
#include "UpgradeTypeObject.h"
#include "UnitTypeObject.h"

void SparObject::initialize()
{
#ifdef USE_SPAR_LOG
  std::cout << "Initializing \"" << *this << "\"" << std::endl;
  for (std::set<SparObject*>::const_iterator it = m_incomingDependencies.begin(); it != m_incomingDependencies.end(); ++it)
  {
    std::cout << "\n  Dependency \"" << **it << "\"" << std::endl;
  }
#endif

  if (dynamic_cast<TechTypeObject*>(this) != NULL)
  {
    TechTypeObject* tech = dynamic_cast<TechTypeObject*>(this);
    BWAPI::TechType techType = tech->getTech();

    BWAPI::UnitType whatResearches = techType.whatResearches();
    if (whatResearches != BWAPI::UnitTypes::None)
    {
      addDependency(whatResearches);
    }
  }
  else if (dynamic_cast<UpgradeTypeObject*>(this) != NULL)
  {
    UpgradeTypeObject* upgrade = dynamic_cast<UpgradeTypeObject*>(this);
    BWAPI::UpgradeType upgradeType = upgrade->getUpgrade();

    BWAPI::UnitType whatUpgrades = upgradeType.whatUpgrades();
    if (whatUpgrades != BWAPI::UnitTypes::None)
    {
      addDependency(whatUpgrades);
    }

    BWAPI::UnitType whatsRequired = upgradeType.whatsRequired(upgrade->getLevel());
    if (whatsRequired != BWAPI::UnitTypes::None)
    {
      addDependency(whatsRequired);
    }
  }
  else if (dynamic_cast<UnitTypeObject*>(this) != NULL)
  {
    UnitTypeObject* unitObject = dynamic_cast<UnitTypeObject*>(this);
    BWAPI::UnitType unitType = unitObject->getUnitType();

    const std::map<BWAPI::UnitType, int>& requiredUnits = unitType.requiredUnits();
    for (std::map<BWAPI::UnitType, int>::const_iterator it = requiredUnits.begin(); it != requiredUnits.end(); ++it)
    {
      addDependency(it->first);
    }

    BWAPI::TechType requiredTech = unitType.requiredTech();
    if (requiredTech != BWAPI::TechTypes::None)
    {
      addDependency(requiredTech);
    }

    const std::pair<BWAPI::UnitType, int> whatBuilds = unitType.whatBuilds();
    if (whatBuilds.first != BWAPI::UnitTypes::None)
    {
      addDependency(whatBuilds.first);
    }
  }
}

void SparObject::addDependency(SparObject* dependency)
{
  m_incomingDependencies.insert(dependency);
  dependency->addOutgoingDependency(this);
}

void SparObject::addDependency(BWAPI::UnitType unitType)
{
  SparObject* dependency = CurrentWorld::getUnitTypeObject(unitType);
  assert(dependency != NULL);
  addDependency(dependency);
}

void SparObject::addDependency(BWAPI::TechType techType)
{
  SparObject* dependency = CurrentWorld::getTechTypeObject(techType);
  assert(dependency != NULL);
  addDependency(dependency);
}

void SparObject::addDependency(BWAPI::UpgradeType upgradeType, int level)
{
  SparObject* dependency = CurrentWorld::getUpgradeTypeObject(upgradeType, level);
  assert(dependency != NULL);
  addDependency(dependency);
}

bool SparObject::allDependenciesAcquired() const
{ 
  bool allDependenciesAcquired = true;
  for (std::set<SparObject*>::const_iterator it = m_incomingDependencies.begin(); allDependenciesAcquired && it != m_incomingDependencies.end(); ++it)
  {
    const SparObject* dependency = *it;
    if (!dependency->isActivated())
      allDependenciesAcquired = false;
  }
  return allDependenciesAcquired;
}

void SparObject::updateOutgoingDependencies()
{
  //SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "SparObject lost: %s", toString(this).c_str());

  for (std::set<SparObject*>::iterator it = m_outgoingDependencies.begin(); it != m_outgoingDependencies.end(); ++it)
  {
    SparObject* outgoingDependency = *it;
    outgoingDependency->onIncomingDependencyChanged(this);
  }
}

// TODO: find a way to trigger "onUnitRecruitable" when dependencies are acquired

void SparObject::onIncomingDependencyChanged(SparObject *)
{
  if (UnitTypeObject * obj = dynamic_cast<UnitTypeObject*>(this))
  {
    obj->update();
  }
}

bool SparObject::isActivated() const
{
  if (dynamic_cast<const TechTypeObject*>(this) != NULL)
  {
    const TechTypeObject* tech = dynamic_cast<const TechTypeObject*>(this);
    return tech->isAcquired();
  }
  else if (dynamic_cast<const UpgradeTypeObject*>(this) != NULL)
  {
    const UpgradeTypeObject* upgrade = dynamic_cast<const UpgradeTypeObject*>(this);
    return upgrade->isAcquired();
  }
  else if (dynamic_cast<const UnitTypeObject*>(this) != NULL)
  {
    const UnitTypeObject* unitObject = dynamic_cast<const UnitTypeObject*>(this);
    if (unitObject->getUnitType().isBuilding())
      return unitObject->isCompleted();
    else
      return unitObject->isCreatable();
  }
  else
  {
    throw std::runtime_error("Invalid object");
  }
}

#pragma endregion