#pragma once
#include "../../../Layer1FSM.h"
#include "../../GroupBehavior.h"
#include "UnitMove/UnitMoveBehavior.h"

/**
 * @brief The GroupMoveBehavior instructs a group of units to move to some destination.
 */
class GroupMoveBehavior : public GroupBehaviorT<GroupMoveBehavior>
{
protected:
  class Goto;
  friend class Goto;

  class Goto : public BehaviorStateT<GroupMoveBehavior, Goto>
  {
  public:
    Goto(GroupMoveBehavior& behavior);

    void onEnter();

    void onExit()
    {}

    // List of events this state register to
    typedef boost::tuple<
    > Events;
    const Events m_events;

    // List of transition this state can take (None since there is only one state in this behavior)
    typedef boost::tuple<> Transitions;
		const Transitions m_transitions;

  protected:
    // Variables

    // Transitions

    // Events
  };

public:
  
  /**
   * @brief MoveBehavior behavior constructor.
   * @param parent The parent process.
   * @param initialPriority The initial priority of the behavior.
   * @param group The persistent unit group to be used by this behavior.
   * @param destination The destination to reach.
   * @param moveOptions The move options.
   */
  GroupMoveBehavior(const Process* parent, unsigned int initialPriority, const PersistentUnitGroup* group,
                    BWAPI::Position destination, const UnitMoveBehavior::MoveOptions& moveOptions)
    : GroupBehaviorT(parent, initialPriority, group)
    , GroupBehavior(parent, initialPriority, group)
    , Behavior(parent, initialPriority)
    , FSM(parent)
    , Process(parent)
    , m_gotoState(*this)
    , m_states(m_gotoState)
    , m_unitAtDestination(*this)
    , m_onSubBehaviorTerminated(*this)
    , m_destination(destination)
    , m_moveOptions(moveOptions)
  {}

  static UnitMoveBehavior::MoveOptions getDefaultMoveOptions(
    const ThreatEvaluator &evaluator = UnitMoveBehavior::DefaultEvaluator, 
    const ObstacleEvaluator &isObstacle = UnitMoveBehavior::DefaultIsObstacle, 
    lookahead_t lookahead = 0, 
    size_t maxDistance = DEFAULT_GROUP_MAX_DISTANCE, 
    size_t refreshRate = 10, 
    size_t givingUpDelay = boost::numeric::bounds<size_t>::highest(), 
    double moveWeight = 5.0, 
    bool threatAware = true, 
    bool attackMove = false)
  {
    return UnitMoveBehavior::getDefaultMoveOptions(evaluator, isObstacle, lookahead, maxDistance, refreshRate, givingUpDelay, moveWeight, threatAware, attackMove);
  }

  typedef boost::tuple<
      Goto&
    > States;
	const States m_states;

  /**
   * @brief The OnUnitAtDestination is raised when a unit reaches the destination.
   */
  DECLARE_EVENT1(OnUnitAtDestination, BWAPI::Unit*);
  OnUnitAtDestination& getUnitAtDestinationEvent() const { return m_unitAtDestination; }

protected:  
  struct BehaviorWrapper
  {
    UnitMoveBehavior* behavior;
    BehaviorT::OnTerminate::SubscriberID subscriberId;
  };

  virtual void _output(std::ostream& out) const
  {
    out << "GroupMoveBehavior(" << getGroup()->getName() << ", " << m_destination << ")";
  }

  virtual void onStart()
  {
    GroupBehaviorT::onStart();
    m_gotoState.enter();
  }

  UnitMoveBehavior* instantiateUnitMoveBehavior(BWAPI::Unit* unit) const;

  virtual void onUnitAddedToGroup(void*, BWAPI::Unit* unit);
  virtual void onUnitRemovedFromGroup(void*, BWAPI::Unit* unit);  
  virtual void onSubBehaviorTerminated(void* data);
  BehaviorWrapper addUnitInMotionList(BWAPI::Unit* unit);

  /**
   * @brief The position (pixel) to reach.
   */
  const BWAPI::Position m_destination;
  /**
   * @brief The move options.
   */
  const UnitMoveBehavior::MoveOptions m_moveOptions;

  /**
   * @brief The set of units in motion.
   */
  std::map<BWAPI::Unit*, BehaviorWrapper> m_unitsInMotion;

  // States
  Goto m_gotoState;

  // Events
  EVENT(OnUnitAtDestination) m_unitAtDestination;
  EVENT_HANDLER(GroupMoveBehavior, onSubBehaviorTerminated) m_onSubBehaviorTerminated;
};