#include "MedicsAndMarinesEngageEnemies.h"
#include "../HealAllies.h"
#include <BWAPI.h>

///////////////////////////////////////////////////
// EngagingEnemies state
///////////////////////////////////////////////////

MedicsAndMarinesEngageEnemies::EngagingEnemies::EngagingEnemies(MedicsAndMarinesEngageEnemies& behavior)
  : m_behavior(behavior)
  , m_marines()
  , m_subBehaviorsReceptacle(*this)
  , m_unitDestroyedReceptacle(*this)
  , m_attackUnitAction(*this)
  , m_enemyUnitInAreaEvent(
      EnemyUnitsInAreaRequest(behavior.m_enemyUnits), 
      UpdateArea<PersistentUnitGroup>(behavior.m_group))
  , m_transitionToSelf(
      *this,
      m_enemyUnitInAreaEvent,
      Eval<NoEnemyUnitInArea, EnemyUnitsInAreaRequest>(NoEnemyUnitInArea(), m_enemyUnitInAreaEvent.getRequest()), 
      NoTransitionAction(),
      *this)
  , m_events(m_enemyUnitInAreaEvent)
  , m_transitions(m_transitionToSelf)
{
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::prepare()
{
  m_enemyUnitInAreaEvent.executeRequest();

  for(PersistentUnitGroup::const_iterator i = m_behavior.m_group->begin();
      i != m_behavior.m_group->end();
      ++i)
  {
    BWAPI::Unit* unit = *i;
    if(unit->getType() == BWAPI::UnitTypes::Terran_Medic)
    {
      HealAllies* healAllies = new HealAllies(getBehavior().getCurrentPriority(), unit, &m_marines);
      SubscriptionBehaviorWrapper* wrapper = new SubscriptionBehaviorWrapper(healAllies);
      wrapper->m_id = m_behavior.addSubBehavior(wrapper);   
      wrapper->m_bid = healAllies->subscribeOnEnd(&m_subBehaviorsReceptacle, &wrapper->m_id);
      healAllies->start();
    }
    else if (unit->getType() == BWAPI::UnitTypes::Terran_Marine)
    {
      m_marines.addUnit(unit);
    }
  }
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::onEnterImpl()
{
	//Maybe some medic logic could be added here.
  if (m_behavior.m_enemyUnits.size() == 0)
    m_behavior.terminate(Behavior::Success);
  else
    FocusFire();
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::OnUnitDestroyed(void * data)
{
  if (m_behavior.m_enemyUnits.size() == 0)
    m_behavior.terminate(Behavior::Success);
  else
    FocusFire();
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::FocusFire()
{
	BWAPI::Position center = m_marines.getCenter();
    BWAPI::Unit* nearestEnemyUnit = m_behavior.m_enemyUnits.getNearest(center);

	UnitManager::getUnitManager().subscribeOnUnitDestroy(nearestEnemyUnit, &m_unitDestroyedReceptacle,0);

	m_attackUnitAction.setUnitToAttack(nearestEnemyUnit);

  Arbitration::getArbitrator().
    submitAction(&m_marines, m_attackUnitAction, Arbitration::ActionImportance(*this));
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::onExitImpl()
{
  for(SubBehaviorList::iterator i = m_behavior.m_subBehaviors.begin();
      i !=  m_behavior.m_subBehaviors.end();
      ++i)
  {
    SubscriptionBehaviorWrapper* w = reinterpret_cast<SubscriptionBehaviorWrapper*>(*i);
    w->m_behavior->unsubscribeOnEnd(w->m_bid);
  }
}

void MedicsAndMarinesEngageEnemies::EngagingEnemies::onSubBehaviorEnded(void* data)
{
  SubBehaviorID id = *(reinterpret_cast<Behavior::SubBehaviorID*>(data));
  m_behavior.removeSubBehavior(id);
}