#include "stdafx.h"
#include "../../../../../../../../PerceptualState/PerceptualState.h"
#include "DragoonUnitAttackBehavior.h"

DragoonUnitAttackBehavior::DragoonUnitAttackBehavior(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_fleeingState(*this)
  , m_idlingState(*this)
  , m_states(m_engagingState, m_fleeingState, m_idlingState)
{
}

// Engaging state //////////////////////////////////////////////////////////////
DragoonUnitAttackBehavior::Engaging::Engaging(DragoonUnitAttackBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_attackAction(*this)
  , m_idlingTransition(*this, NoCondition(), NoTransitionAction(), behavior.m_idlingState)
  , m_lowHealth(
      *this,
      CompositeCondition<HasCriticalHealth,IsWorthFleeing>(
        HasCriticalHealth(behavior.m_unit),
        IsWorthFleeing(behavior.m_unit)),
      NoTransitionAction(),
      behavior.m_fleeingState)
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 0, 1)
  , m_events()
  , m_transitions(m_idlingTransition, m_lowHealth)
  , m_targetEvadedEventHandler(*this)
{
}

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

  //selectTarget();
 /* if(m_context.m_target!= NULL)
  {*/
    m_targetEvadedId = BWAPI::getIndividualUnitEvadeEvent(m_context.m_target).subscribe(&m_targetEvadedEventHandler, NULL);

    onTimer();
  //}
}

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

void DragoonUnitAttackBehavior::Engaging::onTimer()
{
  if(m_context.m_unit->isAttackFrame())
  {
    m_timerEvent.setWaitingTimeBeforeExecution(10);
    m_timerEvent.activate();
  } 
  else
  {
   // if(m_context.m_target= NULL)
    //  m_targetDefeated.testTransition(boost::make_tuple());
    if(!m_lowHealth.testTransition(boost::make_tuple()))
    {
      if(m_context.m_target->exists())
        Layer1FSM::getInstance().getArbitrator().
          submitCommand(m_context.m_unit, AttackUnitCommand(*this, m_context.m_target), Arbitration::ActionImportance(m_context));
      // Not sure if this is the right time to wait
      size_t nextTick = static_cast<size_t>(m_context.m_unit->getGroundWeaponCooldown()+10);
      if(nextTick <= 1)
        nextTick = static_cast<size_t>(m_context.m_unit->getType().groundWeapon().damageCooldown()+1);

      m_timerEvent.setWaitingTimeBeforeExecution(nextTick);
      m_timerEvent.activate();
    }
  }
}

void DragoonUnitAttackBehavior::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));
}

// Fleeing state ///////////////////////////////////////////////////////////////
DragoonUnitAttackBehavior::Fleeing::Fleeing(DragoonUnitAttackBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_moveCommand(*this)
  , m_transition(*this, Not<IsTargeted>(IsTargeted(behavior.m_unit)), NoTransitionAction(), behavior.m_idlingState)
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
  , m_events(m_timerEvent)
  , m_transitions(m_transition)
{
}

void DragoonUnitAttackBehavior::Fleeing::onEnter()
{
  BWAPI::Position position = Spar->getPerceptualState()->getSafeRetreatPosition(m_context.m_unit);
  Layer1FSM::getInstance().getArbitrator().
    submitCommand(m_context.m_unit, MoveCommand(*this, position), Arbitration::ActionImportance(m_context));
}

void DragoonUnitAttackBehavior::Fleeing::onExit()
{
}

void DragoonUnitAttackBehavior::Fleeing::onTimer()
{
  m_transition.testTransition(boost::make_tuple());
}

void DragoonUnitAttackBehavior::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 ////////////////////////////////////////////////////////////////
DragoonUnitAttackBehavior::Idling::Idling(DragoonUnitAttackBehavior& behavior)
  : BehaviorStateT(behavior)
  , m_engagingTransition(*this, NoCondition(), NoTransitionAction(), behavior.m_engagingState)
  , m_transitions(m_engagingTransition)
{
}

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

bool DragoonUnitAttackBehavior::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;
}

//BWAPI::Unit* DragoonUnitAttackBehavior::selectTargetImpl(const std::set<BWAPI::Unit*>& enemies)
//{
//    int minhp = boost::numeric::bounds<int>::highest();
//    int mindist = boost::numeric::bounds<int>::highest();
//    bool threatFound = false;
//    BWAPI::Unit* target = NULL;
//
//    for(std::set<BWAPI::Unit*>::const_iterator i=enemies.begin(); i!=enemies.end(); ++i)
//    {
//      BWAPI::Unit* unit = *i;
//      //Ignore invisible units, eggs and larvae
//      if(!unit->isDetected() 
//        || unit->getType() == BWAPI::UnitTypes::Zerg_Larva 
//        || unit->getType() == BWAPI::UnitTypes::Zerg_Egg)
//        continue;
//      int hp = unit->getHitPoints() + unit->getShields();
//      int dist = unit->getDistance(m_unit->getPosition());
//      //not a threat if the unit is a worker or if the unit is a building that cannot attack
//      bool isThreat = !(unit->getType().isWorker() || (unit->getType().isBuilding() && !unit->getType().canAttack() && unit->getType() != BWAPI::UnitTypes::Terran_Bunker));
//      bool inRange = m_unit->isInWeaponRange(unit);
//
//
//      if( (!threatFound || isThreat)
//        && ((!threatFound && isThreat)
//          || dist < mindist
//          || (inRange && hp < minhp)))
//      {
//        minhp = hp;
//        mindist = dist;
//        target = unit;
//        if(isThreat)
//          threatFound = true;
//      }
//    }
//    if(target != NULL)
//      std::string temp = target->getType().getName();
//    return target;
//}
