#include "stdafx.h"
#include "DodgeBehavior.h"
#include "../Layer1FSM.h"
#include "../../../../PerceptualState/PerceptualState.h"
#include <stdlib.h>
#include <string>

static const int DODGE_DISTANCE = 90;
static const int SAFE_POSITION_RANGE = 3;

///////////////////////////////////////////////////
// Unit in safe State

DodgeBehavior::Safe::Safe(DodgeBehavior& behavior)
  : BehaviorStateT(behavior)  
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
  , m_transition(
	  *this,
	  HasCriticalHealth(behavior.m_unit),
	  NoTransitionAction(),
	  behavior.m_criticalState)
  , m_events(m_timerEvent)
  , m_transitions(m_transition)
{
}

void DodgeBehavior::Safe::onEnter()
{
}

void DodgeBehavior::Safe::onExit()
{
}

///////////////////////////////////////////////////
// Unit in critical State

DodgeBehavior::Critical::Critical(DodgeBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_moveCommand(*this)
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 10, 10)
  , m_transitionToSelf(
	  *this,
	  InDanger(behavior.m_unit),
	  NoTransitionAction(),
	  *this)
  , m_transitionToSafeState(
    *this,
    Not<ShouldBeDodging>(ShouldBeDodging(HasCriticalHealth(behavior.m_unit), InDanger(behavior.m_unit))),
    NoTransitionAction(), 
    behavior.m_safeState)
  , m_events(m_timerEvent)
  , m_transitions(m_transitionToSelf,m_transitionToSafeState)
{
}

void DodgeBehavior::Critical::onEnter()
{
  BWAPI::Position destination = m_context.getSafeDodgePosition(m_context.m_unit);
  Layer1FSM::getInstance().getArbitrator().submitCommand(m_context.m_unit, MoveCommand(*this, destination), Arbitration::ActionImportance(m_context));
}

void DodgeBehavior::Critical::onExit()
{
}

BWAPI::Position DodgeBehavior::getSafeDodgePosition(BWAPI::Unit* unit)
{
  BWAPI::TilePosition unitPosition = BWAPI::TilePosition(unit->getPosition());
  int x, y;
  BWAPI::TilePosition bestPosition;
  Threat currentBestInfluence;
  currentBestInfluence.ground = boost::numeric::bounds<int>::lowest();
  currentBestInfluence.air = boost::numeric::bounds<int>::lowest();
  const InfluenceMapRef<Threat>::map_ptr self = Spar->getPerceptualState()->getSelfThreat();
  const InfluenceMapRef<Threat>::map_ptr enemy = Spar->getPerceptualState()->getEnemyThreat();
  for(int i= -SAFE_POSITION_RANGE; i < SAFE_POSITION_RANGE + 1; ++i)
  {
    for(int j= -SAFE_POSITION_RANGE; j < SAFE_POSITION_RANGE + 1; ++j)
    {
      x= unitPosition.x() + i;
      y= unitPosition.y() + j;
      if ((BWAPI::TilePosition(x, y)).isValid())
      {
        const Threat& selfThreat = self->getData(x, y);
        const Threat& enemyThreat = enemy->getData(x, y);
        double groundDiff = selfThreat.ground - enemyThreat.ground;
        double airDiff = selfThreat.air - enemyThreat.air;
        if ((unit->getType().isFlyer() && airDiff >= currentBestInfluence.air) 
          || (!unit->getType().isFlyer() && groundDiff >= currentBestInfluence.ground))
        {
          currentBestInfluence.ground = groundDiff;
          currentBestInfluence.air = airDiff;
          bestPosition = BWAPI::TilePosition(BWAPI::TilePosition(x, y));
        }
      }
    }
  }

  BWAPI::Position destination = BWAPI::Position(bestPosition) - unit->getPosition();
  float length = sqrt(static_cast<float>(destination.x() * destination.x() + destination.y() * destination.y()));
  float newX = destination.x() / length; 
  float newY = destination.y() / length;
  float distance = DODGE_DISTANCE;
  destination = unit->getPosition() + BWAPI::Position(static_cast<int>(newX * distance), 
                                                      static_cast<int>(newY * distance));

  return destination;
}