#pragma once
#include "../../../../Commands/MoveCommand.h"
#include "../../../../Pathfinding.h"
#include "../../../UnitBehavior.h"
#include "../../../Utils/TimerState.h"
#include "UnitMoveBehavior.h"
#include <functional>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

/**
 * @brief The RetreatUnitBehavior instructs a unit to move to some destination.
 */
class RetreatUnitBehavior : public UnitBehaviorT<RetreatUnitBehavior>
{
public:
  static double DefaultEvaluator(BWAPI::TilePosition)
  {
    return 0; // No threat
  }

  static bool DefaultIsObstacle(BWAPI::TilePosition, BWAPI::TilePosition)
  {
    return false;
  }

  class MoveOptions
  {
  public:
    MoveOptions(const ThreatEvaluator &evaluator, const ObstacleEvaluator &isObstacle, int maxDistance, size_t refreshRate)
      : m_maxDistance(maxDistance)
      , m_refreshRate(refreshRate)
      , m_threatEvaluator(evaluator)
      , m_isObstacle(isObstacle)
    {}

    /**
     * @brief The maximum distance before considering that the unit has reached its destination - note that if a group of units
     *        all get sent to the same destination, some of the units will end up far from the destination. Suggested: 4 * TILE_SIZE if the unit is in a group and TILE_SIZE for single units.
     */
    // TODO: this should be calculated automatically from the group's size and composition
    int m_maxDistance;
    /**
     * @brief The number of frames before successive revisions of the current path. Must be > 0.
     */
    size_t m_refreshRate;
    /**
     * @brief An evaluation function that estimates the threat of a specific tile.
     */
    ThreatEvaluator m_threatEvaluator;
    /**
     * @brief An evaluation function that determines whether a movement from a tile to a neighboring tile is possible.
     */
    ObstacleEvaluator m_isObstacle;

  private:
    //Prevents using the assignment operator
    MoveOptions& operator=(const MoveOptions&);
  };

  static MoveOptions getDefaultMoveOptions(BWAPI::Unit* unit)
  {
    return MoveOptions(
      pathfinding::GeneralThreatEvaluator(unit), 
      pathfinding::GeneralObstacleEvaluator(unit),
      DEFAULT_UNIT_MAX_DISTANCE,
      10);
  }

protected:
  class Goto;
  friend class Goto;

  class Goto : public TimerState<RetreatUnitBehavior, RetreatUnitBehavior::Goto>
  {
  public:
    Goto(RetreatUnitBehavior& behavior, size_t refreshDelay)
      : TimerState(behavior, refreshDelay, refreshDelay)
      , m_events(m_timerEvent)
      , m_transitions()
    {
      m_currentTarget = m_smoothPath.end();
    }

    void onTimer();

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

    typedef boost::tuple<> Transitions;
		const Transitions m_transitions;

  protected:
    void submitMoveCommand(BWAPI::Position pos)
    {
      // Can be replaced eventually by commands so that arbitrator can properly arbitrate
      m_context.m_unit->move(pos);
    }
    std::vector<BWAPI::TilePosition> m_smoothPath;
    std::vector<BWAPI::TilePosition>::const_iterator m_currentTarget;
  };

public:
  RetreatUnitBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* unit, const MoveOptions& moveOptions)
    : UnitBehaviorT(parent, initialPriority, unit)
    , UnitBehavior(parent, initialPriority, unit)
    , Behavior(parent, initialPriority)
    , FSM(parent)
    , Process(parent)
    , m_moveOptions(moveOptions)
    , m_gotoState(*this, moveOptions.m_refreshRate)
    , m_states(m_gotoState)
  {
  }

  typedef boost::tuple<
    Goto&
  > States;
  const States m_states;
	
protected:
  virtual void onStart()
  {
    UnitBehaviorT::onStart();
    m_gotoState.enter();
  }

  // States
  Goto m_gotoState;

  /**
   * @brief The options related to the motion.
   */
  const MoveOptions m_moveOptions;
};
