#include "stdafx.h"
#include "L2Collect.h"
#include "../Layer2Managers.h"

// Must be smaller than the probe sight range (256)
static const size_t MINERAL_PROXIMITY = 128;

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

  m_gatherBehaviorTerminatedSubscriberId = m_gatherBehavior.getTerminateEvent().subscribe(&m_gatherBehaviorTerminatedEventHandler, NULL);

  std::pair<BaseLocation::crystal_iterator, BaseLocation::crystal_iterator> crystals = m_baseLocation->getLastKnownCrystalsLeft();
  if(crystals.first == crystals.second)
  {
    terminate(Success);
  } 
  else
  {
    bool accessibleMineralFound = false;
    BaseLocation::crystal_iterator mineralIt = crystals.first;
    while(mineralIt != crystals.second && !accessibleMineralFound)
    {
      accessibleMineralFound = (*mineralIt)->isAccessible();
      ++mineralIt;
    }

    if(accessibleMineralFound)
    {
      const Region* baseRegion = m_baseLocation->getParent();
      for(PersistentUnitGroup::const_iterator it = getGroup()->begin(); it != getGroup()->end(); ++it)
      {
        assert((*it)->getType() == BWAPI::UnitTypes::Protoss_Probe);
        if(baseRegion != Spar->getPerceptualState()->getMap()->getRegion((*it)->getTilePosition()))
        {
          m_movingGroup.insert(*it);
        } 
        else
        {
          m_gatheringGroup.insert(*it);
        }        
      }
    } 
    else
    {
      for(PersistentUnitGroup::const_iterator it = getGroup()->begin(); it != getGroup()->end(); ++it)
      {
        assert((*it)->getType() == BWAPI::UnitTypes::Protoss_Probe);
        m_movingGroup.insert(*it);
      }
    }
    
    if(m_movingGroup.size() > 0 && m_moveBehavior == NULL)
      startMoveBehavior();

    m_gatherBehavior.start();
  }
}

void L2CollectMinerals::onUnitAddedToGroup(void*, BWAPI::Unit* unit)
{
  assert(unit->getType() == BWAPI::UnitTypes::Protoss_Probe);
  bool accessibleMineralFound = false;
  
  std::pair<BaseLocation::crystal_iterator, BaseLocation::crystal_iterator> crystals = m_baseLocation->getLastKnownCrystalsLeft();
  BaseLocation::crystal_iterator mineralIt = crystals.first;
  while(mineralIt != crystals.second && !accessibleMineralFound)
  {
    accessibleMineralFound = (*mineralIt)->isAccessible();
    ++mineralIt;
  }

  if(!accessibleMineralFound || m_baseLocation->getParent() != Spar->getPerceptualState()->getMap()->getRegion(unit->getTilePosition()))
  {
    m_movingGroup.insert(unit);
    if(m_movingGroup.size() > 0 && m_moveBehavior == NULL)
      startMoveBehavior();
  } else {
    m_gatheringGroup.insert(unit);
  }
}

void L2CollectMinerals::onUnitRemovedFromGroup(void*, BWAPI::Unit* unit)
{
  bool removed = m_movingGroup.remove(unit);
  if(!removed)
    m_gatheringGroup.remove(unit);
}

void L2CollectMinerals::startMoveBehavior()
{
  assert(m_moveBehavior == NULL);

  std::pair<BaseLocation::crystal_iterator, BaseLocation::crystal_iterator> crystals = m_baseLocation->getLastKnownCrystalsLeft();
  
  // TODO: Real threat evaluator
  m_moveBehavior = new GroupMoveBehavior(
    this, 
    getPriority(), 
    &m_movingGroup,
    (*crystals.first)->getPosition(), 
    GroupMoveBehavior::getDefaultMoveOptions(
      UnitMoveBehavior::DefaultEvaluator,
      UnitMoveBehavior::DefaultIsObstacle,
      0,                  // lookahead
      MINERAL_PROXIMITY,  // max distance
      5,                  // refresh rate
      5));                // giving up delay
  m_moveBehavior->zombifyOnTerminate();
  m_unitAtDestinationSubscriberId = m_moveBehavior->getUnitAtDestinationEvent().subscribe(&m_unitAtDestinationEventHandler, NULL);
  m_moveBehaviorTerminatedSubscriberId = m_moveBehavior->getTerminateEvent().subscribe(&m_moveBehaviorTerminatedEventHandler, NULL);
  m_moveBehavior->start();
}

void L2CollectMinerals::onUnitAtDestination(void* data, BWAPI::Unit* unit)
{
  unused(data);
  m_movingGroup.remove(unit);
  m_gatheringGroup.insert(unit);
}

void L2CollectMinerals::onMoveBehaviorTerminated(void* data)
{
  unused(data);
  m_moveBehavior->getUnitAtDestinationEvent().unsubscribe(m_unitAtDestinationSubscriberId);
  m_moveBehavior->getTerminateEvent().unsubscribe(m_moveBehaviorTerminatedSubscriberId);
  m_moveBehavior = NULL;
}

void L2CollectMinerals::onGatherBehaviorTerminated(void*)
{
  if(isExecuting())
  {
    m_gatherBehavior.getTerminateEvent().unsubscribe(m_gatherBehaviorTerminatedSubscriberId);
    if(m_moveBehavior != NULL)
    {
      m_moveBehavior->getUnitAtDestinationEvent().unsubscribe(m_unitAtDestinationSubscriberId);
      m_moveBehavior->getTerminateEvent().unsubscribe(m_moveBehaviorTerminatedSubscriberId);
      m_moveBehavior = NULL;
    }
  }

  terminate(Success);
}

void L2CollectMinerals::onCancelImplementation()
{
  L2Collect::onCancelImplementation();

  if(isExecuting())
    m_gatherBehavior.getTerminateEvent().unsubscribe(m_gatherBehaviorTerminatedSubscriberId);
  m_gatherBehavior.externalTerminate();
  if(m_moveBehavior != NULL)
  {
    m_moveBehavior->externalTerminate();
    m_moveBehavior->getUnitAtDestinationEvent().unsubscribe(m_unitAtDestinationSubscriberId);
    m_moveBehavior->getTerminateEvent().unsubscribe(m_moveBehaviorTerminatedSubscriberId);
    m_moveBehavior = NULL;
  }

  terminate(Cancelled);
}

void L2CollectMinerals::onEndImplementation()
{
  L2Collect::onEndImplementation();

  if(m_moveBehavior != NULL)
  {
    m_moveBehavior->getUnitAtDestinationEvent().unsubscribe(m_unitAtDestinationSubscriberId);
    m_moveBehavior->getTerminateEvent().unsubscribe(m_moveBehaviorTerminatedSubscriberId);
    m_moveBehavior = NULL;
  }
}


/////////////////////////////////////
// CollectGas
/////////////////////////////////////

//TODO: Manage multiple geyser in one location

void L2CollectGas::onStartImplementation()
{
  L2GroupTacticalAction::onStartImplementation();
  std::pair<BaseLocation::unit_type_const_iterator, BaseLocation::unit_type_const_iterator> assimilator = 
    this->m_baseLocation->getOwnUnits(BWAPI::UnitTypes::Protoss_Assimilator);
  if(assimilator.first != assimilator.second)
  {
    for(PersistentUnitGroup::const_iterator it = getGroup()->begin(); it!=getGroup()->end() ; ++it)
    {
      (*it)->gather((*assimilator.first)->getUnit());
    }   
  }
}

void L2CollectGas::onCancelImplementation()
{
  L2Collect::onCancelImplementation();
  terminate(Cancelled);
}

void L2CollectGas::onEndImplementation()
{
  L2Collect::onEndImplementation();
}

void L2CollectGas::onUnitAddedToGroup(void*, BWAPI::Unit* unit)
{
  std::pair<BaseLocation::unit_type_const_iterator, BaseLocation::unit_type_const_iterator> assimilator = 
    this->m_baseLocation->getOwnUnits(BWAPI::UnitTypes::Protoss_Assimilator);
  if(assimilator.first != assimilator.second)
  {
    unit->gather((*assimilator.first)->getUnit());
  }
}

void L2CollectGas::onUnitRemovedFromGroup(void*, BWAPI::Unit*)
{
}
