#include "stdafx.h"
#include "LearningCombatBehavior.h"
#include "../../Layer1FSM.h"
#include "../../../../../PerceptualState/PerceptualState.h"

///////////////////////////////////////////////////
// No Action state

BWAPI::Unit* findClosestEnemy(BWAPI::Unit* unit);
BWAPI::Unit* findRandomEnemy(BWAPI::Unit* unit);
BWAPI::Unit* findStrongestEnemy(BWAPI::Unit* unit);
BWAPI::Unit* findDyingEnemy(BWAPI::Unit* unit);

LearningCombatBehavior::NoAction::NoAction(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::NoAction::onEnter()
{
  //Do nothing
}

///////////////////////////////////////////////////
// AttackRandom state
LearningCombatBehavior::AttackRandom::AttackRandom(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::AttackRandom::onEnter()
{
  for(PersistentUnitGroup::const_iterator it = m_context.getGroup()->begin() ; it != m_context.getGroup()->end() ; ++it)
  {
    std::map<BWAPI::Unit*,BWAPI::Unit*>::iterator target = m_context.mapOfTarget.find((*it));
    target->second = findRandomEnemy(*it);
    if(m_context.fleeingUnit.find(*it)==m_context.fleeingUnit.end() && target->second != NULL)
    {
      (*it)->attack(target->second);
    }
  }
}

  ///////////////////////////////////////////////////
// AttackStrongest state
LearningCombatBehavior::AttackStrongest::AttackStrongest(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::AttackStrongest::onEnter()
{
  //Determine the strongest enemy and set all unit to attack it
  for(PersistentUnitGroup::const_iterator it = m_context.getGroup()->begin() ; it != m_context.getGroup()->end() ; ++it)
  {
    std::map<BWAPI::Unit*,BWAPI::Unit*>::iterator target = m_context.mapOfTarget.find((*it));
    target->second = findStrongestEnemy(*it);
    if(m_context.fleeingUnit.find(*it)==m_context.fleeingUnit.end() && target->second != NULL)
    {
      (*it)->attack(target->second);
    }
  }
}

  ///////////////////////////////////////////////////
// AttackDying state
LearningCombatBehavior::AttackDying::AttackDying(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::AttackDying::onEnter()
{
  for(PersistentUnitGroup::const_iterator it = m_context.getGroup()->begin() ; it != m_context.getGroup()->end() ; ++it)
  {
    std::map<BWAPI::Unit*,BWAPI::Unit*>::iterator target = m_context.mapOfTarget.find((*it));
    target->second = findDyingEnemy(*it);
    if(m_context.fleeingUnit.find(*it)==m_context.fleeingUnit.end() && target->second != NULL)
    {
      (*it)->attack(target->second);
    }
  }
}

  ///////////////////////////////////////////////////
// AttackClosest state
LearningCombatBehavior::AttackClosest::AttackClosest(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::AttackClosest::onEnter()
{
  //for each unit make it attack its closest enemy
  for(PersistentUnitGroup::const_iterator it = m_context.getGroup()->begin() ; it != m_context.getGroup()->end() ; ++it)
  {
    std::map<BWAPI::Unit*,BWAPI::Unit*>::iterator target = m_context.mapOfTarget.find((*it));
    target->second = findClosestEnemy(*it);
    if(m_context.fleeingUnit.find(*it)==m_context.fleeingUnit.end() && target->second != NULL)
    {
      (*it)->attack(target->second);
    }
  }
}

  ///////////////////////////////////////////////////
// Flee state
LearningCombatBehavior::Flee::Flee(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
//, m_moveCommand(*this)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::Flee::onEnter()
{
  //find a safe retreat position and give order to move there
  BWAPI::Position destination = Spar->getPerceptualState()->
    getSafeRetreatPosition(*(m_context.getGroup()->begin())); //, m_context.m_enemyUnits);

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

  ///////////////////////////////////////////////////
// RemoveTargetedUnit state
LearningCombatBehavior::RemoveTargetedUnit::RemoveTargetedUnit(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::RemoveTargetedUnit::onEnter()
{
  //find targeted unit and make them flee
  for(PersistentUnitGroup::const_iterator it = m_context.getGroup()->begin() ; it != m_context.getGroup()->end() ; ++it)
  {
    if((*it)->isUnderAttack() && 0.30 < ((*it)->getHitPoints())/((*it)->getType().maxHitPoints()))
    {
      m_context.fleeingUnit.insert(*it);
      BWAPI::Position destination = Spar->getPerceptualState()->
        getSafeRetreatPosition(*(m_context.getGroup()->begin()));
      (*it)->move(destination);
    }
  }
}

  ///////////////////////////////////////////////////
// PutBackSafeUnit state
LearningCombatBehavior::PutBackSafeUnit::PutBackSafeUnit(LearningCombatBehavior& behavior)
: BehaviorStateT(behavior)
, m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
, m_transitionToARState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackRandomState)
, m_transitionToASState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackStrongestState)
, m_transitionToADState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackDyingState)
, m_transitionToACState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_attackClosestState)
, m_transitionToFleeState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_fleeState)
, m_transitionToRemoveState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_removeTargetedUnitState)
, m_transitionToPutBackState(
                        *this,
                        NoCondition(), 
                        NoTransitionAction(), 
                        behavior.m_putBackSafeUnitState)
, m_events(m_timerEvent)
, m_transitions(m_transitionToARState,m_transitionToASState,m_transitionToADState,m_transitionToACState,m_transitionToFleeState,m_transitionToRemoveState,m_transitionToPutBackState)
{
}

void LearningCombatBehavior::PutBackSafeUnit::onEnter()
{
  //find fleeing unit and make them attack again
  for(std::set<BWAPI::Unit*>::iterator it = m_context.fleeingUnit.begin() ; it!=m_context.fleeingUnit.end() ; ++it)
  {
    if(!(*it)->isUnderAttack())
    {
      std::map<BWAPI::Unit*,BWAPI::Unit*>::iterator target = m_context.mapOfTarget.find((*it));
      assert(target != m_context.mapOfTarget.end());
      if(target->second!=NULL)
      {
        if(target->second->exists()){
          (*it)->attack(target->second);
        }
        else
        {
          target->second = NULL;
        }
      }
      if(target->second==NULL)
      {
        (*it)->stop();
      }
      m_context.fleeingUnit.erase(*it);
    }
  }
}

BWAPI::Unit* findClosestEnemy(BWAPI::Unit* unit)
{
  BWAPI::Unit* target = NULL;
  int range = 256;
  while(target == NULL && range <= 1024)
  {
    std::set<BWAPI::Unit*> units = Spar->getPerceptualState()->getEnemiesNearUnit(unit,range);
    int closest = 1025;
    for(std::set<BWAPI::Unit*>::iterator it = units.begin() ; it != units.end() ; ++it)
    {
      int tmpDistance = unit->getDistance(*it);
      if(tmpDistance < closest){
        closest = tmpDistance;
        target = *it;
      }
    }
    if(target!=NULL)
    {
      return target;
    }
    else
    {
      range = range*2;
    }
  }
  return NULL;
}

BWAPI::Unit* findDyingEnemy(BWAPI::Unit* unit)
{
  BWAPI::Unit* target = NULL;
  int range = 256;
  while(target == NULL && range <= 1024)
  {
    std::set<BWAPI::Unit*> units = Spar->getPerceptualState()->getEnemiesNearUnit(unit,range);
    int closest = 1025;
    for(std::set<BWAPI::Unit*>::iterator it = units.begin() ; it != units.end() ; ++it)
    {
      int tmpDistance = (*it)->getHitPoints();
      if(tmpDistance < closest){
        closest = tmpDistance;
        target = *it;
      }
    }
    if(target!=NULL)
    {
      return target;
    }
    else
    {
      range = range*2;
    }
  }
  return NULL;
}

BWAPI::Unit* findStrongestEnemy(BWAPI::Unit* unit)
{
  BWAPI::Unit* target = NULL;
  int range = 256;
  while(target == NULL && range <= 1024)
  {
    std::set<BWAPI::Unit*> units = Spar->getPerceptualState()->getEnemiesNearUnit(unit,range);
    int closest = -1;
    for(std::set<BWAPI::Unit*>::iterator it = units.begin() ; it != units.end() ; ++it)
    {
      int tmpDistance = (*it)->getType().groundWeapon().damageAmount() + (*it)->getType().airWeapon().damageAmount();
      if(tmpDistance > closest){
        closest = tmpDistance;
        target = *it;
      }
    }
    if(target!=NULL)
    {
      return target;
    }
    else
    {
      range = range*2;
    }
  }
  return NULL;
}

BWAPI::Unit* findRandomEnemy(BWAPI::Unit* unit)
{
  std::set<BWAPI::Unit*> units = Spar->getPerceptualState()->getEnemiesNearUnit(unit,1024);
  int nb = rand() % (units.size() + 1);
  std::set<BWAPI::Unit*>::iterator it = units.begin();
  for(int i = 0 ; i<nb ; i++)
  {
    ++it;
  }
  if(it==units.end())
  {
    return NULL;
  }
  else
  {
    return (*it);
  }
}