#include "stdafx.h"
#include "../../../Layer1Reactive/Layer1FSM/Behaviors/General/Scout/MotionScoutBehavior.h"
#include "../Layer2Managers.h"
#include "L2Scout.h"
#include <cmath>

// TODO: L2Scout could use exclusively Move behaviors (so we could delete the Scout behaviors)

void L2Scout::onStartImplementation()
{
  L2GroupTacticalAction::onStartImplementation();

  BWAPI::Unit* unit = NULL;
  //
  // Get the unit to use for scouting
  //
  if (!getGroup()->empty())
  {
    assert(getGroup()->size() == 1);
    unit = *getGroup()->begin();
  }
  else
  {
    int minDistance = boost::numeric::bounds<int>::highest();
    const std::set<BWAPI::Unit*>& unitSet = BWAPI::Broodwar->self()->getUnits();
    for(std::set<BWAPI::Unit*>::const_iterator it = unitSet.begin(); it!=unitSet.end(); ++it)
    {
      //TODO: is there anything else to test ?
      if((*it)->exists() && !(*it)->getType().isBuilding() && (*it)->isCompleted() && 
        (dynamic_cast<Layer2UnitData&>(BWAPI::getUnitData(*it)).getCurrentAction() == NULL ||
        dynamic_cast<Layer2UnitData&>(BWAPI::getUnitData(*it)).getCurrentAction()->getPriority() < m_priority) &&
        ((*it)->getType().isFlyer() || (*it)->getPosition().hasPath(m_location->getCurrentCenter())))
      {
        int distance= (*it)->getPosition().getApproxDistance(m_location->getCurrentCenter());
        if(unit == NULL || 
          ((*it)->getType().isWorker() && !unit->getType().isWorker()) || 
          ((*it)->getType().isWorker() == unit->getType().isWorker() &&
          (distance + (((*it)->isCarryingMinerals() || (*it)->isCarryingGas()) ? 20 * TILE_SIZE : 0) < 
            minDistance + ((unit->isCarryingMinerals() || unit->isCarryingGas()) ? 20 * TILE_SIZE : 0)))) // 20 tiles handicap for probes carrying stuff
        {
          unit = (*it);
          minDistance = distance;
        }
      }
    }

    assert(unit!=NULL);
    bool reserved = addUnit(unit);
    // TODO: pick another one
    unused(reserved); // remove when reserved is finally used
    assert(reserved);
  }

  //
  // Start the scouting behavior with the selected unit
  //
  assert(unit == *getGroup()->begin());
  //if (unit->getType() == BWAPI::UnitTypes::Protoss_Observer)
  //{
    m_scoutBehavior = new MotionScoutBehavior(this, m_priority, m_location, unit);
  //}
  //else
  //{
  //  m_scoutBehavior = new GradientDescentScoutBehavior(this, m_priority, m_location, unit);
  //}

  m_scoutArrivedSubscriberId = m_scoutBehavior->getScoutArrivedEvent().subscribe(&m_scoutArrivedEventHandler, NULL);
  m_scoutTerminatedSubscriberId = m_scoutBehavior->getTerminateEvent().subscribe(&m_scoutTerminatedEventHandler, NULL);
  m_scoutBehavior->start();
}

void L2Scout::onUnitAddedToGroup(void*, BWAPI::Unit*)
{
  // Should only happen if reserveUnit() is called
}

void L2Scout::onUnitRemovedFromGroup(void*, BWAPI::Unit*)
{
  assert(getGroup()->empty()); // Only one unit allowed for now
  terminate(AllUnitsDestroyed);
}

void L2Scout::onCancelImplementation()
{
  L2GroupTacticalAction::onCancelImplementation();
  terminate(Cancelled);
}

void L2Scout::onEndImplementation()
{
  if(m_scoutBehavior!=NULL)
  {
    m_scoutBehavior->getScoutArrivedEvent().unsubscribe(m_scoutArrivedSubscriberId);
    m_scoutBehavior->getTerminateEvent().unsubscribe(m_scoutTerminatedSubscriberId);
  }
  L2GroupTacticalAction::onEndImplementation();
}

void L2Scout::onScoutArrived(void*)
{
  SPAR_LOG(LogTypes.LAYER_2_DECISION, "Scout has arrived!");

  m_scoutArrivedEvent.raise(this,m_location,m_scoutBehavior->getUnit());
}

void L2Scout::onScoutTerminated(void*)
{
  terminate(m_scoutBehavior->getTerminationMode());
}