#include "stdafx.h"
#include "GradientDescentScoutBehavior.h"
#include "../Behaviors/General/Move/GroupMoveBehavior.h"
#include "../Layer1FSM.h"
#include "../../../../PerceptualState/PerceptualState.h"

GradientDescentScoutBehavior::GradientDescentScoutBehavior(const Process* parent, unsigned int initialPriority, StaticLocation* location, BWAPI::Unit* scoutingUnit)
  : ScoutBehaviorT(parent, initialPriority, location, scoutingUnit)
  , ScoutBehavior(parent, initialPriority, location, scoutingUnit)
  , UnitBehavior(parent, initialPriority, scoutingUnit)
  , Behavior(parent, initialPriority)
  , FSM(parent)
  , Process(parent)
  , m_scoutingState(*this)
  , m_goToLocationState(*this)
  , m_states(m_goToLocationState, m_scoutingState)
{
}

///////////////////////////////////////////////////
// GoToLocation State
///////////////////////////////////////////////////
GradientDescentScoutBehavior::GoToLocation::GoToLocation(GradientDescentScoutBehavior& behavior)
: BehaviorStateT(behavior)
//, m_moveCommand(*this)
, m_locationReached(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToScouting(*this,
     UnitReachedLocation(behavior.getUnit(), behavior.m_locationToScout, DEFAULT_UNIT_MAX_DISTANCE), 
     //NoTransitionAction(), 
     RaiseScoutArrived(&m_context),
     behavior.m_scoutingState)
, m_events(m_locationReached)
, m_transitions(m_transitionToScouting)
{}

void GradientDescentScoutBehavior::GoToLocation::onEnter()
{
  //Set destination
  const BWAPI::Position position = m_context.m_locationToScout->getCurrentCenter(); 
  //Give command
  Layer1FSM::getInstance().getArbitrator().submitCommand(m_context.m_unit, MoveCommand(*this, position), Arbitration::ActionImportance(m_context));
}

///////////////////////////////////////////////////
// Scouting state
///////////////////////////////////////////////////
GradientDescentScoutBehavior::Scouting::Scouting(GradientDescentScoutBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_moveCommand(*this)
  , m_newMove(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 10, 10)
  , m_transitionToSelf(*this,
                       NoCondition(), 
                       NoTransitionAction(), 
                       *this)
  , m_events(m_newMove)
  , m_transitions(m_transitionToSelf)
  , m_maxY(BWAPI::Broodwar->mapHeight() * TILE_SIZE)
  , m_maxX(BWAPI::Broodwar->mapWidth() * TILE_SIZE)
{
}

void GradientDescentScoutBehavior::Scouting::onEnter()
{
  //Get next destination
  BWAPI::Position destination(-m_context.m_locationToScout->getCurrentCenter().x(),m_context.m_locationToScout->getCurrentCenter().y()) ;
  //Get potential menace
  std::list<BWAPI::Unit*> danger;
  std::set<BWAPI::Unit*> enemyGroup = Spar->getPerceptualState()->getEnemiesNearUnit(m_context.m_unit,BWAPI::WeaponTypes::Arclite_Cannon.maxRange());
  for (std::set<BWAPI::Unit*>::const_iterator it = enemyGroup.begin(); it != enemyGroup.end(); ++it)
  { 
    //if((*it)->getType().canAttack()){
    if(m_context.m_unit->getType().isFlyer()){
      if((*it)->getType().airWeapon() != BWAPI::WeaponTypes::None){
        if(m_context.m_unit->getDistance((*it)) < (*it)->getType().airWeapon().maxRange() + 64){
          danger.push_back((*it));
        }
      }
    }
    else{
      if((*it)->getType().groundWeapon() != BWAPI::WeaponTypes::None && !(*it)->getType().isWorker()){
        if(m_context.m_unit->getDistance((*it)) < (*it)->getType().groundWeapon().maxRange() + 64){
          danger.push_back((*it));
        }
      }	  
    }
  }
  //}
  //If the menace exist move away from it
  if(!danger.empty())
  {
    destination = Spar->getPerceptualState()->getSafeRetreatPosition(m_context.m_unit);
  }
  else
  {
    const BWAPI::TilePosition tile = m_context.m_locationToScout->getOldestVisibleTile().first;
    destination = BWAPI::getCenter(tile);
  }

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

//Add fleeX and fleeY to the scout's current position to get the destination.
//Note: checks to make sure the destination is not outside the map!
BWAPI::Position GradientDescentScoutBehavior::Scouting::getFleeDestination(double fleeX, double fleeY)
{
  double destX = m_context.m_unit->getPosition().x() + fleeX;
  if(destX < 0)
    destX = 0;
  else if(destX > m_maxX)
    destX = m_maxX;

  double destY = m_context.m_unit->getPosition().y() + fleeY;
  if(destY < 0)
    destY = 0;
  else if(destY > m_maxY)
    destY = m_maxY;

  return BWAPI::Position(static_cast<int>(destX), static_cast<int>(destY));
}