#include "stdafx.h"
#include "DefaultUnitAttackBehavior.h"

DefaultUnitAttackBehavior::DefaultUnitAttackBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* unit)
  : UnitAttackBehaviorT(parent, initialPriority, unit)
  , UnitAttackBehavior(parent, initialPriority, unit)
  , UnitBehavior(parent, initialPriority, unit)
  , Behavior(parent, initialPriority)
  , FSM(parent)
  , Process(parent)
  , m_engagingState(*this)
  , m_idlingState(*this)
  , m_states(m_engagingState, m_idlingState)
{
}

bool DefaultUnitAttackBehavior::selectTarget(const std::set<BWAPI::Unit*>& enemies)
{
  BWAPI::Unit* oldTarget = m_target;
  m_target = selectTargetImpl(enemies);
  if(getCurrentState() == &m_engagingState)
  {
    if(m_target != oldTarget)
      m_engagingState.targetChanged(oldTarget);
  } 
  else if (getCurrentState() == &m_idlingState)
  {
    if(m_target != NULL)
      m_idlingState.onTargetChanged();
  }

  return m_target != NULL;
}

// Engaging state //////////////////////////////////////////////////////////////
DefaultUnitAttackBehavior::Engaging::Engaging(DefaultUnitAttackBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_attackAction(*this)
  , m_idlingTransition(*this, NoCondition(), NoTransitionAction(), behavior.m_idlingState)
  //, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
  , m_events()
  , m_transitions(m_idlingTransition)
  , m_targetEvadedEventHandler(*this)
{
}

void DefaultUnitAttackBehavior::Engaging::onEnter()
{
  assert(m_context.m_target != NULL);

  m_targetEvadedId = BWAPI::getIndividualUnitEvadeEvent(m_context.m_target).subscribe(&m_targetEvadedEventHandler, NULL);

  Layer1FSM::getInstance().getArbitrator().
    submitCommand(m_context.m_unit, AttackUnitCommand(*this, m_context.m_target), Arbitration::ActionImportance(m_context));
}

void DefaultUnitAttackBehavior::Engaging::onExit()
{
  if(m_context.m_target != NULL)
    BWAPI::getIndividualUnitEvadeEvent(m_context.m_target).unsubscribe(m_targetEvadedId);
  m_context.m_target = NULL;
}

//void DefaultUnitAttackBehavior::Engaging::onTimer()
//{}

void DefaultUnitAttackBehavior::Engaging::targetChanged(BWAPI::Unit* oldTarget)
{
  if(oldTarget != NULL)
    BWAPI::getIndividualUnitEvadeEvent(oldTarget).unsubscribe(m_targetEvadedId);
  if(m_context.m_target != NULL)
    m_targetEvadedId = BWAPI::getIndividualUnitEvadeEvent(m_context.m_target).subscribe(&m_targetEvadedEventHandler, NULL);
  else
    m_idlingTransition.testTransition(boost::make_tuple());
  
  Layer1FSM::getInstance().getArbitrator().
    submitCommand(m_context.m_unit, AttackUnitCommand(*this, m_context.m_target), Arbitration::ActionImportance(m_context));
}

void DefaultUnitAttackBehavior::Engaging::onTargetEvaded(void*)
{
  BWAPI::Unit* oldTarget = m_context.m_target;
  m_context.m_target = NULL;
  targetChanged(oldTarget);
  //TODO: We should probably select a new target now, but we don't have the set of enemies.
}

// Idling state ////////////////////////////////////////////////////////////////
DefaultUnitAttackBehavior::Idling::Idling(DefaultUnitAttackBehavior& behavior)
  : BehaviorStateT(behavior)
  , m_engagingTransition(*this, NoCondition(), NoTransitionAction(), behavior.m_engagingState)
  , m_transitions(m_engagingTransition)
{
}

void DefaultUnitAttackBehavior::Idling::onTargetChanged()
{
  // Transition to Engaging state
  m_engagingTransition.testTransition(boost::make_tuple());
}
