#include "stdafx.h"
#include "../../../../../PerceptualState/PerceptualState.h"
#include "../../Pathfinding.h"
#include "../General/Move/UnitMove/ThreatAwareUnitMoveBehavior.h"
#include "../General/Move/UnitMove/UnitMoveBehavior.h"
#include "../General/Move/UnitMove/RetreatUnitBehavior.h"
#include "CorsairHarassmentBehavior.h"

// TODO: pathfinding when attacking a nearby target (to avoid going inside a defensive structure defense radius)

// TODO: avoid choosing the same destination when no path was previously found... (when entering goto state again)

CorsairHarassmentBehavior::CorsairHarassmentBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* unit, Location * harassArea)
  : UnitBehaviorT(parent, initialPriority, unit)
  , UnitBehavior(parent, initialPriority, unit)
  , Behavior(parent, initialPriority)

  , FSM(parent)
  , Process(parent)
  , m_harassArea(*this, harassArea)
  , m_initState(*this)
  , m_gotoState(*this)
  , m_harassingState(*this)
  , m_fleeingState(*this)
  , m_states(m_initState, m_gotoState, m_harassingState, m_fleeingState)
  , m_unitInDanger(*this)
  , m_currentScore(BWAPI::TilePositions::None)
{
}

CorsairHarassmentBehavior::TileScore CorsairHarassmentBehavior::findBestTile(CorsairHarassmentBehavior::TileEvaluator tileEvaluator) const
{
  const BWAPI::TilePosition current = m_unit->getTilePosition();
  TileScore best(BWAPI::TilePositions::None);
  for(int y = 0; y < BWAPI::Broodwar->mapHeight(); ++y)
  {
    for(int x = 0; x < BWAPI::Broodwar->mapWidth(); ++x)
    {
      BWAPI::TilePosition tile(x, y);
      if(current != tile && m_harassArea->isInLocation(tile))
      {
        TileScore score = tileEvaluator(tile);
        if(!best.tile.isValid() || score.totalScore > best.totalScore)
        {
          best = score;
        }
      }
    }
  }
  return best;
}

CorsairHarassmentBehavior::TileScore CorsairHarassmentBehavior::calculateAggregateScore(const BWAPI::TilePosition &tile) const
{ 
  // Future enemy air, air-to-air vulnerability, distance, age

  CorsairHarassmentBehavior::TileScore score(tile);
  const InfluenceMapRef<Threat>::map_ptr th = Spar->getPerceptualState()->getEnemyThreat();
  const InfluenceMapRef<Vulnerability>::map_ptr vuln = Spar->getPerceptualState()->getVulnerability();
  const InfluenceMapRef<Visibility>::map_ptr vis = Spar->getPerceptualState()->getVisibility();

  if (th->getData(tile).futureAir == 0)
  {
    // Scale stuff in [0, 1]

    score.vulnerabilityScore = static_cast<double>(vuln->getData(tile)._airToAir);

    const double distance = tile.getDistance(m_unit->getTilePosition());
    //const double ratio = distance * TILE_SIZE / BWAPI::getMapDiagonalLength();
    score.distanceScore = std::min(distance / 32., 1.0);

    const int lastVisible = vis->getData(tile).lastVisible;
    score.ageScore = (lastVisible == boost::numeric::bounds<int>::lowest()) ? 1.0 :
      (lastVisible == boost::numeric::bounds<int>::highest() ? 0.0 : static_cast<double>(BWAPI::Broodwar->getFrameCount() - lastVisible) / 10000.);

    static const double WEIGHT_DISTANCE_WRT_VULNERABILITY = 0.1;
    static const double WEIGHT_AGE_WRT_VULNERABILITY = 0.1;

    score.totalScore = (static_cast<double>(rand()) / (RAND_MAX * 1000.0)) + score.vulnerabilityScore + WEIGHT_DISTANCE_WRT_VULNERABILITY * score.distanceScore + WEIGHT_AGE_WRT_VULNERABILITY * score.ageScore;
  }

  return score;
}

//bool CorsairHarassmentBehavior::Harassing::compareVulnerability(const BWAPI::TilePosition & best, const BWAPI::TilePosition &tile)
//{
//  PerceptualState * ps = Spar->getPerceptualState();
//  const Vulnerability::value_type vulnBest = ps->getVulnerabilityLevel(best)._airToAir;
//  const Vulnerability::value_type vulnTile = ps->getVulnerabilityLevel(tile)._airToAir;
//  if(std::abs(vulnBest - vulnTile) < 0.0001)
//  {
//    BWAPI::TilePosition current = m_context.m_unit->getTilePosition();
//    //return current.getDistance(best) > current.getDistance(tile);
//
//    // Temporary fix to avoid having Corsairs stuck at the same place
//    return current.getDistance(best) < current.getDistance(tile);
//  }
//
//  return vulnBest < vulnTile;
//}

BWAPI::Unit * CorsairHarassmentBehavior::findNearbyVulnerableUnit() const
{
  const int ATTACK_RANGE = m_unit->getType().airWeapon().maxRange() + TILE_SIZE;

  std::pair<BWAPI::Unit *, Vulnerability::value_type> weakest = std::make_pair(static_cast<BWAPI::Unit *>(0), 0.0);
  std::set<BWAPI::Unit *> nearbyUnits = BWAPI::Broodwar->getUnitsInRadius(m_unit->getPosition(), ATTACK_RANGE);
  for(std::set<BWAPI::Unit *>::const_iterator it = nearbyUnits.begin(); it != nearbyUnits.end(); ++it)
  {
    if((*it)->getPlayer() == BWAPI::Broodwar->enemy())
    {
      Vulnerability::value_type vuln = Vulnerability::getAirToAir(*it);
      // vuln == 0 if unit should not be attacked
      if(vuln > weakest.second)
        weakest = std::make_pair(*it, vuln);
    }
  }
  return weakest.first;
}

///////////////////////////////////////////////////
// Init state
///////////////////////////////////////////////////

void CorsairHarassmentBehavior::Init::onEnter()
{
   //if(m_context.m_harassArea->isInLocation(m_context.m_unit->getPosition()))
   //{
   //  m_transitionToHarassing.testTransition(boost::make_tuple());
   //}
   //else
   //{
     m_context.m_currentScore = m_context.findHarassingPositionInArea();
     m_context.m_currentTarget = getCenter(m_context.m_currentScore.tile);
     m_transitionToGoto.testTransition(boost::make_tuple());
   //}
}

///////////////////////////////////////////////////
// Goto state
///////////////////////////////////////////////////

Behavior* CorsairHarassmentBehavior::Goto::createSubBehavior()
{
  UnitMoveBehavior::MoveOptions options = ThreatAwareUnitMoveBehavior::getDefaultMoveOptions(
    pathfinding::GeneralThreatEvaluator(m_context.m_unit), 
    pathfinding::ConsiderThreatAsObstacle(m_context.m_unit), 
    0,                                                  // lookahead
    m_context.m_unit->getType().airWeapon().maxRange(), // max distance
    2,                                                  // refresh rate
    40);                                                // giving up delay 
  options.m_attackMove = true;
  ThreatAwareUnitMoveBehavior* behavior = new ThreatAwareUnitMoveBehavior(&m_context, m_context.getCurrentPriority(), m_context.m_unit, m_context.m_currentTarget, options);
  return behavior;
}

void CorsairHarassmentBehavior::Goto::onEnter()
{
  if (!m_context.m_currentTarget.isValid())
  {
     m_context.m_currentScore = m_context.findHarassingPositionInArea();
     if (m_context.m_currentScore.tile.isValid())
     {
       m_context.m_currentTarget = getCenter(m_context.m_currentScore.tile);
     }
     else
     {
       m_context.m_currentTarget = m_context.m_harassArea->getCurrentCenter();
     }
  }

  WrapperState::onEnter();
}

void CorsairHarassmentBehavior::Goto::onTimer()
{
  BWAPI::Unit* vulnerableUnit = m_context.findNearbyVulnerableUnit();
  if (vulnerableUnit)
  {
    m_transitionToHarassing.testTransition(boost::make_tuple());
  }
}

void CorsairHarassmentBehavior::Goto::onSubBehaviorTerminated()
{
  m_context.m_currentScore = TileScore(BWAPI::TilePositions::None);
  m_context.m_currentTarget = BWAPI::Positions::None;

  BWAPI::Unit* vulnerableUnit = m_context.findNearbyVulnerableUnit();
  if (vulnerableUnit)
  {
    m_transitionToHarassing.testTransition(boost::make_tuple());
  }

  WrapperState::onSubBehaviorTerminated();
}

///////////////////////////////////////////////////
// Fleeing state
///////////////////////////////////////////////////

Behavior * CorsairHarassmentBehavior::Fleeing::createSubBehavior()
{
  return new RetreatUnitBehavior(
    &m_context, 
    m_context.getCurrentPriority(), 
    m_context.m_unit, 
    RetreatUnitBehavior::getDefaultMoveOptions(m_context.m_unit));
}

///////////////////////////////////////////////////
// Harassing state
///////////////////////////////////////////////////

void CorsairHarassmentBehavior::Harassing::onTimer()
{
  const InfluenceMapRef<Threat>::map_ptr th = Spar->getPerceptualState()->getEnemyThreat();
  if(th->getData(m_context.m_unit->getTilePosition()).futureAir > 0)
  {
    m_transitionToFleeing.testTransition(boost::make_tuple());
  }
  else
  {
    BWAPI::Unit * target = m_context.findNearbyVulnerableUnit();
    if(target)
    {
      m_context.m_unit->attack(target);
    }
    else
    {
      m_context.m_currentScore = m_context.findHarassingPositionInArea();
      m_context.m_currentTarget = getCenter(m_context.m_currentScore.tile);
      
      if(m_context.m_currentTarget.isValid())
      {
        m_transitionToGoto.testTransition(boost::make_tuple());
      }
    }
  }
}
