#pragma once
#include "UnitMoveBehavior.h"
#include "../../../../Commands/AttackMoveCommand.h"
#include "../../../../../../../BWAPI_Addon.h"
#include "../../../../../../../Utils/Logger.h"
#include "../../../../../../../Scheduler/SchedulerTaskHelper.h"
#include <BWAPI.h>
#include <cassert>
#include <functional>
#include <boost/unordered_map.hpp>

/**
 * @brief The ThreatAwareUnitMoveBehavior is a specialization for a threat-aware move behavior.
 */
class ThreatAwareUnitMoveBehavior : public UnitMoveBehaviorT<ThreatAwareUnitMoveBehavior>
{
protected:
  struct EuclideanDistance : std::binary_function<const BWAPI::TilePosition&, const BWAPI::TilePosition&, double>
  {
    double operator()(const BWAPI::TilePosition& pos, const BWAPI::TilePosition& goal) const
    {
      return pos.getDistance(goal);
    }
  };

  class AStarHeuristic : public std::binary_function<const BWAPI::TilePosition&, const BWAPI::TilePosition&, double>
  {
    typedef boost::unordered_map<BWAPI::TilePosition, double, BWAPI::tile_position_hash> H;
    H m_map;
    const double m_moveWeight;
    
  public:
    AStarHeuristic(const double moveWeight)
      : m_map()
      , m_moveWeight(moveWeight)
    {
    }

    double operator()(const BWAPI::TilePosition& pos, const BWAPI::TilePosition& goal) const
    {
      H::const_iterator iter = m_map.find(pos);
      if (iter != m_map.end())
      {
        return iter->second;
      }
      else
      {
        return m_moveWeight * EuclideanDistance()(pos, goal);
      }
    }

    void update(const BWAPI::TilePosition& pos, double h)
    {
      m_map[pos] = h;
    }

    typedef H::const_iterator const_iterator;

    const_iterator find(const BWAPI::TilePosition& pos) const
    {
      return m_map.find(pos);
    }

    const_iterator end() const
    {
      return m_map.end();
    }

  private:
    //const member prevent the use of the assigment operator
    AStarHeuristic& operator= (const AStarHeuristic&);
  };

  class Goto;
  friend class Goto;
  friend class UnitMoveBehaviorT<ThreatAwareUnitMoveBehavior>;

  class Goto : public UnitMoveBehaviorT::Goto
  {
  public:
    Goto(ThreatAwareUnitMoveBehavior& behavior, size_t refreshDelay, const double moveWeight);

    void onEnter();
    void onExit();
    void onTimer();
    void onFrameDebug();

    typedef boost::tuple<
      TimerEvent&
#ifdef NON_FINAL_VERSION
      ,FrameDebugEvent&
#endif
    > Events;
    const Events m_events;

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

    struct WeightedPathElement
    {
      BWAPI::TilePosition m_tile;
      double m_threat;
    
      WeightedPathElement(const BWAPI::TilePosition &tile, double threat) : m_tile(tile), m_threat(threat) {}
      
      operator BWAPI::TilePosition() const
      {
        return m_tile;
      }

      int x() const { return m_tile.x(); }
      int y() const { return m_tile.y(); }
    };
    typedef std::vector<WeightedPathElement> WeightedPath;
    //typedef std::vector<const BWAPI::TilePosition> SmoothPath;

  protected:
    bool hasGreaterThreatAlongPath();
    Path calculatePath(const BWAPI::TilePosition current);
    void onNewPathFound(const Path& path);
    WeightedPath generateWeightedPath(const Path& path) const;

    AStarHeuristic m_h;
    //ScoreMap m_h;
    WeightedPath m_fullPath;
    std::vector<BWAPI::TilePosition> m_smoothPath;
    std::vector<BWAPI::TilePosition>::const_iterator m_currentTarget;
    bool m_isRetreatPath;
    volatile bool m_updateInProgress;
  };

public:
  ThreatAwareUnitMoveBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* unit,
                      BWAPI::Position destination, const MoveOptions& moveOptions);

  typedef boost::tuple<
      Goto&
    > States;

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

  // States
  Goto m_gotoState;

public:
	const States m_states;
};


