#pragma once
#include "../General/Move/UnitMove/UnitMoveBehavior.h"
#include "../Utils/TimerState.h"
#include "../Utils/WrapperState.h"
#include <boost/bind.hpp>

class Location;

class CorsairHarassmentBehavior : public UnitBehaviorT<CorsairHarassmentBehavior>
{
protected:
  class Init;
  class Goto;
  class Harassing;
  class Fleeing;

  struct TileScore
  {
    TileScore(BWAPI::TilePosition t)
      : tile(t), distanceScore(0.), ageScore(0.), vulnerabilityScore(0.), totalScore(0.)
    {}

    BWAPI::TilePosition tile;

    double distanceScore;
    double ageScore;
    double vulnerabilityScore;
    double totalScore;
  };

  typedef boost::function<TileScore(const BWAPI::TilePosition &tile)> TileEvaluator;
  TileScore findBestTile(TileEvaluator tileEvaluator) const;
  TileScore calculateAggregateScore(const BWAPI::TilePosition& tile) const;

  TileScore findHarassingPositionInArea() const
  {
    return findBestTile(boost::bind(&CorsairHarassmentBehavior::calculateAggregateScore, this, _1));
  }

  BWAPI::Unit * findNearbyVulnerableUnit() const;

  //bool compareVulnerability(const BWAPI::TilePosition & best, const BWAPI::TilePosition &tile) const;

public:
  CorsairHarassmentBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* unit, Location * harassArea);

  // Needs to be precisely defined
  DECLARE_EVENT(UnitInDanger);
  UnitInDanger& getUnitInDangerEvent() { return m_unitInDanger; }

protected:
  EVENT(UnitInDanger) m_unitInDanger;

  virtual void _output(std::ostream& out) const
  {
    out << "CorsairHarassmentBehavior(" << *m_unit << ")";
  }
  
  virtual void onStart()
  {
    UnitBehaviorT::onStart();
    m_initState.enter();
  }

  friend class Init;
  friend class Goto;
  friend class Harassing;
  friend class Fleeing;
  
  class Init : public BehaviorStateT<CorsairHarassmentBehavior, Init>
  {
  public:
    Init(CorsairHarassmentBehavior &behavior) 
    : BehaviorStateT(behavior),
      m_transitionToGoto(*this, NoCondition(), NoTransitionAction(), behavior.m_gotoState),
      m_transitionToHarassing(*this, NoCondition(), NoTransitionAction(), behavior.m_harassingState),
      m_transitions(m_transitionToGoto, m_transitionToHarassing)
    {}

    void onEnter();
    void onExit() {}

    typedef boost::tuple<
          > Events;
		Events m_events;
    
		typedef boost::tuple<
          Transition<Init, NoCondition, NoTransitionAction, Goto>&,
          Transition<Init, NoCondition, NoTransitionAction, Harassing>&
		  > Transitions;
		Transitions m_transitions;
  protected:
    Transition<Init, NoCondition, NoTransitionAction, Goto> m_transitionToGoto;
    Transition<Init, NoCondition, NoTransitionAction, Harassing> m_transitionToHarassing;
  };

  class Goto : public WrapperState<CorsairHarassmentBehavior, Goto, NoTransitionAction, Goto>
  {
  public:
    Goto(CorsairHarassmentBehavior &behavior) 
      : WrapperState(behavior, NoTransitionAction(), *this)
      , m_transitionToHarassing(*this, NoCondition(), NoTransitionAction(), behavior.m_harassingState)
      , m_transitions(m_transitionToDestination, m_transitionToHarassing)
      , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 24, 24)
      , m_events(m_timerEvent)
    {}

    Behavior * createSubBehavior();

    void onEnter();
    void onTimer();

    void onSubBehaviorTerminated();

    typedef SCHEDULER_EVENT_HANDLER(Goto, onTimer) TimerEvent;
    typedef boost::tuple<
      TimerEvent&
    > Events;
		Events m_events;
    
		typedef boost::tuple<
      Transition<Goto, NoCondition, NoTransitionAction, Goto>&,
      Transition<Goto, NoCondition, NoTransitionAction, Harassing>&
		> Transitions;
		Transitions m_transitions;

  protected:
    Transition<Goto, NoCondition, NoTransitionAction, Harassing> m_transitionToHarassing;

    TimerEvent m_timerEvent;
  };

  class Fleeing : public WrapperState<CorsairHarassmentBehavior, Fleeing, NoTransitionAction, Harassing>
  {
  public:
    Fleeing(CorsairHarassmentBehavior &behavior) : WrapperState(behavior, NoTransitionAction(), behavior.m_harassingState) {}

    Behavior * createSubBehavior();
  };

  class Harassing : public TimerState<CorsairHarassmentBehavior, Harassing>
  {
  public:
    Harassing(CorsairHarassmentBehavior &behavior)
      : TimerState(behavior, behavior.m_unit->getType().airWeapon().damageCooldown(), 1),
        m_events(m_timerEvent),
        //m_unitDiscoveredHandler(*this),
        m_transitionToGoto(*this, NoCondition(), NoTransitionAction(), behavior.m_gotoState),
        m_transitionToFleeing(*this, NoCondition(), NoTransitionAction(), behavior.m_fleeingState),
        m_transitions(m_transitionToGoto, m_transitionToFleeing)
    {}
    void onTimer();
    
    typedef boost::tuple<
      TimerEvent&
          > Events;
		Events m_events;
    
		typedef boost::tuple<
          Transition<Harassing, NoCondition, NoTransitionAction, Goto>&,
          Transition<Harassing, NoCondition, NoTransitionAction, Fleeing>&
    > Transitions;
		Transitions m_transitions;
  protected:
    BWAPI::Position findVulnerablePosition() const;
    BWAPI::Position findSafeSpot() const;

    Transition<Harassing, NoCondition, NoTransitionAction, Goto> m_transitionToGoto;
    Transition<Harassing, NoCondition, NoTransitionAction, Fleeing> m_transitionToFleeing;
  };

public:
  typedef boost::tuple<
      Init&,
      Goto&,
      Harassing&,
      Fleeing&
    > States;
	const States m_states;
protected:
  Init m_initState;
  Goto m_gotoState;
  Harassing m_harassingState;
  Fleeing m_fleeingState;

  const AutoPtr<Location> m_harassArea;
  BWAPI::Position m_currentTarget;
  TileScore m_currentScore;
};