#include "stdafx.h"
#include "Layer3Script.h"
#include "L3BaseLocation.h"
#include "../../../PerceptualState/Resources/ResourceManager.h"
#include "../../../PerceptualState/PerceptualState.h"
#include <ctime>

Layer3Script::Layer3Script(Layer3TacticalRecognition& layer3TacticalRecognition, Layer2Actions& layer2Actions)
  : Layer3Tactical(layer3TacticalRecognition, layer2Actions)
  , m_unitReleasedEventHandler(*this)
  , m_unitCompletedEventHandler(*this)
  , m_manageResources(this)
  , m_buildPylons(this)
  , m_buildGateways(this)
  , m_buildExpansions(this)
  , m_buildCannons(this)
  , m_progressTech(this)
  , m_recruitArmy(this)
  , m_manageArmy(this)
  , m_mainBase(NULL)
{
}

Layer3Script::~Layer3Script()
{
}

void Layer3Script::onStart()
{
  Layer3Tactical::onStart();

  srand(static_cast<unsigned int>(time(NULL)));

  assert(Spar->getPerceptualState()->getMap()->getOwnStartBaseLocation() != NULL);
  assert(Spar->getPerceptualState()->getMap()->getEnemyPossibleStartBaseLocations().size() > 0);

  m_layer2Actions.getUnitIdleEvent().subscribe(&m_unitReleasedEventHandler, NULL);
  Spar->getPerceptualState()->getUnitCompletedEvent().subscribe(&m_unitCompletedEventHandler, NULL);

  m_mainBase = dynamic_cast<L3BaseLocation*>(Spar->getPerceptualState()->getMap()->getOwnStartBaseLocation());
  assert(m_mainBase != NULL);

  // Execute sub-processes
  m_buildCannons.start();
  m_buildExpansions.start();
  m_buildGateways.start();
  m_buildPylons.start();
  m_manageArmy.start();
  m_manageResources.start();
  m_progressTech.start();
  m_recruitArmy.start();
}

void Layer3Script::onEnd()
{
  Layer3Tactical::onEnd();
}

void Layer3Script::onUnitCompleted(void*, BWAPI::Unit* unit)
{
  if (!unit->getType().isBuilding() && unit->getType() != BWAPI::UnitTypes::Protoss_Probe)
  {
    m_manageArmy.addUnit(unit);
  }
}

void Layer3Script::onUnitReleased(void*, BWAPI::Unit* unit, const GroupTacticalAction*)
{
  // This callback takes place when a unit has been released from an action (typically when the action terminates)

  // Could also keep a stack of previous actions, per unit? (for example if a scouting probe was reserved for a building action)

  assert(!unit->getType().isBuilding());

  if (unit->getType() == BWAPI::UnitTypes::Protoss_Probe)
  {
    L3BaseLocation* baseLocation = getNearestOwnMiningBaseLocation(unit->getPosition());
    if (baseLocation != NULL)
      baseLocation->getCollectMinerals()->addUnit(unit);
    // TODO: Else mine somewhere accessible with no enemy
  }
  else if (!unit->getType().isBuilding())
  {
    m_manageArmy.addUnit(unit);
  }
}

void Layer3Script::onFrame()
{
  Layer3Tactical::onFrame();
}

int Layer3Script::countSupplyForOneRoundTroops() const
{
  const Map* map = Spar->getPerceptualState()->getMap();
  int supply = 0;
  supply += map->getNbOwnUnits(BWAPI::UnitTypes::Protoss_Nexus) * BWAPI::UnitTypes::Protoss_Probe.supplyRequired();
  supply += map->getNbOwnUnits(BWAPI::UnitTypes::Protoss_Gateway) * BWAPI::UnitTypes::Protoss_Zealot.supplyRequired();
  supply += map->getNbOwnUnits(BWAPI::UnitTypes::Protoss_Robotics_Facility) * BWAPI::UnitTypes::Protoss_Reaver.supplyRequired();
  supply += map->getNbOwnUnits(BWAPI::UnitTypes::Protoss_Stargate) * BWAPI::UnitTypes::Protoss_Corsair.supplyRequired();
  return supply;
}

L3BaseLocation* Layer3Script::getNearestOwnMiningBaseLocation(BWAPI::Position position) const
{
  BaseLocation* bestLocation = NULL;
  double minDistance = boost::numeric::bounds<double>::highest();

  std::pair<Map::base_location_iterator, Map::base_location_iterator> baseLocations = Spar->getPerceptualState()->getMap()->getBaseLocations();
  for (; baseLocations.first != baseLocations.second; ++baseLocations.first)
  {
    BaseLocation* candidate = *baseLocations.first;
    if (candidate->getNbCompletedOwnUnits(BWAPI::UnitTypes::Protoss_Nexus) > 0 &&
        candidate->getLastKnownNbCrystalsLeft() > 0)
    {
      // TODO: nearest (walkable) base locations first
      double distance = position.getDistance(candidate->getCurrentCenter());
      if (distance < minDistance)
      {
        minDistance = distance;
        bestLocation = candidate;
      }
    }
  }
  return dynamic_cast<L3BaseLocation*>(bestLocation);
}

ResourcesAcquisitionPercentage Layer3Script::computeTargetResourcesAcquisitionRatio() const
{
  // Should depend on current actions (resources requests?) / current units composition
  //m_layer2Actions.getCurrentResourcesRequests()
  //m_recruitArmy.getCurrentUnitsComposition()

  // At some point anyway it is better to invest probes in collecting gas (even if said gas piles up)
  // than to over-saturate a mineral line. The gas can be later dumped on mass high templars / archons.

  // TODO: compute correct ratio

  ResourcesAcquisitionPercentage currentRatio = m_manageResources.getTargetResourcesAcquisitionPercentage();

  if (currentRatio.getPctGas() > 0 || 
      Spar->getPerceptualState()->getMap()->getNbOwnUnits(BWAPI::UnitTypes::Protoss_Cybernetics_Core) > 0)
  {
    if (Spar->getPerceptualState()->getMap()->getNbCompletedOwnUnits(BWAPI::UnitTypes::Protoss_Templar_Archives) > 0)
      return ResourcesAcquisitionPercentage(2.0, 0.35);
    else
      return ResourcesAcquisitionPercentage(2.0, 0.25);
  }
  else
  {
    return ResourcesAcquisitionPercentage(2.0, 0.0); // Minerals only
  }
}

void Layer3Script::updateResourcesAcquisitionPercentage()
{
  m_manageResources.updateResourcesAcquisitionPercentage(computeTargetResourcesAcquisitionRatio());
}

void Layer3Script::onDependentTechsUpdate()
{
  // Called from m_progressTech

  updateResourcesAcquisitionPercentage();
}
