#pragma once
#include "../../../Behaviors/UnitBehavior.h"
#include "../../../Conditions/IsIdle.h"
#include "../../../Conditions/UnitLoaded.h"
#include "../../../Conditions/InDanger.h"
#include "../../../Conditions/ShieldRecharged.h"
#include "../../../Conditions/AtLeastOneUnit.h"
#include "../../../Commands/MoveCommand.h"
#include "../../../Commands/LoadUnitCommand.h"
#include "../../../Commands/UnloadAllUnitsAtPositionCommand.h"

static const double MAX_REAVER_DISTANCE = 5;

class IsDecelerating : public Condition
{
public:
  IsDecelerating(BWAPI::Unit* unit, BWAPI::Unit* reaver)
    : m_unit(unit), m_reaver(reaver)
  {
    assert(reaver->getType() == BWAPI::UnitTypes::Protoss_Reaver);
  }
  bool evaluate() const
  {
    return m_unit->isBraking() || !m_unit->isMoving() || (m_unit->getDistance(m_reaver) > MAX_REAVER_DISTANCE);	
  }

protected:
  BWAPI::Unit* m_unit;
  BWAPI::Unit* m_reaver;
};

class NoTarget : public Condition
{
public:
  NoTarget(BWAPI::Unit* unit)
    : m_unit(unit)
  {
  }

  bool evaluate() const
  {
    // TODO: hardcoded stuff

    //Get all units in a range X
    int range = m_unit->getType().groundWeapon().maxRange();
    bool rep = true;
    bool shield = true;
    if(m_unit->getType() == BWAPI::UnitTypes::Protoss_Reaver){
      range = 256+32;
      rep = m_unit->getScarabCount() >= 5;
      shield = (m_unit->getShields()/m_unit->getType().maxShields())>0.5;
    }
    std::set<BWAPI::Unit*> enemyGroup = Spar->getPerceptualState()->getEnemiesNearUnit(m_unit,range);

    return enemyGroup.size() == 0 && rep && shield;
  }

protected:
  BWAPI::Unit* m_unit;

};

class ShuttleControlBehavior : public UnitBehaviorT<ShuttleControlBehavior>
{
protected:
  class Waiting;
  class PickUp;
  class Drop;
  class GetDistance;
  friend class Waiting;
  friend class PickUp;
  friend class Drop;
  friend class GetDistance;

  class Waiting : public BehaviorStateT<ShuttleControlBehavior, Waiting>
  {
  public:
    Waiting(ShuttleControlBehavior& behavior);
    void onEnter();
    void onExit() {}

    void onTimer()
    {
      m_transitionToPickUpStateDanger.testTransition(boost::make_tuple());
      m_transitionToPickUpStateTarget.testTransition(boost::make_tuple());
      m_transitionToSelf.testTransition(boost::make_tuple());
    }

    typedef SCHEDULER_EVENT_HANDLER(Waiting, onTimer) TimerEvent;

    typedef boost::tuple<
      TimerEvent&
    > Events;
		const Events m_events;

    typedef boost::tuple<
      Transition<Waiting, InDanger, NoTransitionAction, PickUp>&,
      Transition<Waiting, NoTarget, NoTransitionAction, PickUp>&,
      Transition<Waiting, IsDecelerating, NoTransitionAction, Waiting>&
    > Transitions;
		const Transitions m_transitions;

  protected:
    // Variables

    // Actions
    //MoveCommand m_moveCommand;

    // Transitions
    // TODO : m_reaver in Danger! Transition Waiting from PickUp
    TimerEvent m_timerEvent;
    Transition<Waiting, InDanger, NoTransitionAction, PickUp> m_transitionToPickUpStateDanger;
    Transition<Waiting, NoTarget, NoTransitionAction, PickUp> m_transitionToPickUpStateTarget;
    Transition<Waiting, IsDecelerating, NoTransitionAction, Waiting> m_transitionToSelf;
  };

  class PickUp : public BehaviorStateT<ShuttleControlBehavior, PickUp>
  {
  public:
    PickUp(ShuttleControlBehavior& behavior);
    void onEnter();
    void onExit() {}

    void onTimer()
    {
      m_transitionToDropState.testTransition(boost::make_tuple());
    }

    typedef SCHEDULER_EVENT_HANDLER(PickUp, onTimer) TimerEvent;

    typedef boost::tuple<
      TimerEvent&
    > Events;
		const Events m_events;

    typedef boost::tuple<
      Transition<PickUp, UnitLoaded, NoTransitionAction, Drop>&
    > Transitions;
		const Transitions m_transitions;
  protected:
    // Variables

    // Actions
    //LoadUnitCommand m_loadUnitCommand;

    // Transitions
    // TODO : m_reaver is PickedUp! Transition from PickUp to drop
    TimerEvent m_unitIsLoaded;
    Transition<PickUp, UnitLoaded, NoTransitionAction, Drop> m_transitionToDropState;
  };

  class Drop : public BehaviorStateT<ShuttleControlBehavior, Drop>
  {
  public:
    Drop(ShuttleControlBehavior& behavior);
    void onEnter();
    void onExit() {}

    void onTimer()
    {
      m_transitionToWaitingState.testTransition(boost::make_tuple());
    }

    typedef SCHEDULER_EVENT_HANDLER(Drop, onTimer) TimerEvent;

    typedef boost::tuple<
      TimerEvent&
    > Events;
		const Events m_events;

    typedef boost::tuple<
      Transition<Drop, UnitOnGround, NoTransitionAction, GetDistance>&
    > Transitions;
		const Transitions m_transitions;
  protected:
    // Variables

    // Actions
    //UnloadAllUnitsAtPositionCommand m_unloadAllUnitsAtPosition; 

    // Transitions
    // TODO : m_reaver is Unloaded! Transition from PickUp to drop
    TimerEvent m_unitIsUnloaded;
    Transition<Drop, UnitOnGround, NoTransitionAction, GetDistance> m_transitionToWaitingState;
  };

  class GetDistance : public BehaviorStateT<ShuttleControlBehavior, GetDistance>
  {
  public:
    GetDistance(ShuttleControlBehavior& behavior);
    void onEnter();
    void onExit() {}

    void onTimer()
    {
      m_transitionToWaitingState.testTransition(boost::make_tuple());
    }

    typedef SCHEDULER_EVENT_HANDLER(GetDistance, onTimer) TimerEvent;

    typedef boost::tuple<
      TimerEvent&
    > Events;
		const Events m_events;

    typedef boost::tuple<
      Transition<GetDistance, NoCondition, NoTransitionAction, Waiting>&
    > Transitions;
		const Transitions m_transitions;	
  protected:
    // Variables

    // Actions
    //MoveCommand m_moveCommand;

    // Transitions
    TimerEvent m_moveActionGiven;
    Transition<GetDistance, NoCondition, NoTransitionAction, Waiting> m_transitionToWaitingState;
  };

public:
  ShuttleControlBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* shuttle, BWAPI::Unit* reaver)
    : UnitBehaviorT(parent, initialPriority, shuttle)
    , UnitBehavior(parent, initialPriority, shuttle)
    , Behavior(parent, initialPriority)
    , FSM(parent)
    , Process(parent)
    , m_reaver(reaver)
    , m_waitingState(*this), m_pickUpState(*this), m_dropState(*this), m_getDistanceState(*this)
    , m_states(m_waitingState, m_pickUpState, m_dropState, m_getDistanceState)
  {
  }

  typedef boost::tuple<
      Waiting&,
      PickUp&,
      Drop&,
      GetDistance&
    > States;
	const States m_states;

protected:
  virtual void _output(std::ostream& out) const
  {
    out << "ShuttleControlBehavior(" << *m_unit << ")";
  }

  virtual void onStart()
  {
    UnitBehaviorT::onStart();
    m_waitingState.enter();
  }

  // Arguments

  // Variables
  BWAPI::Unit* m_reaver;
  std::set<BWAPI::Unit*> m_enemyUnits;

  // States
  Waiting m_waitingState;
  PickUp m_pickUpState;
  Drop m_dropState;
  GetDistance m_getDistanceState;
};