#pragma once
#include "../../../../Conditions/IsAttacking.h"
#include "../../../../Conditions/ReachedDestination.h"
#include "../../../Utils/FinalState.h"
#include "../../../General/EngageEnemies/EngageEnemiesBehavior.h"
#include "UnitAttack/UnitAttackBehavior.h"
#include <hash_map>

class Location;

//TODO: Group attack behavior should coordinate the individual attack behaviors, as in:
// - choosing a common target to focus fire onto
// - keeping some kind of formation
// Individual behaviors only consider the "local" situation, i.e. microing the individual unit involved

//TODO: Observer logic should be put in another file, this class is getting messy

class GroupAttackBehavior : public EngageEnemiesBehaviorT<GroupAttackBehavior>
{
protected:
  class GoToLocation;
  class Engaging;
  //class Terminate;
  typedef FinalState<GroupAttackBehavior> Terminate;

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

  // GoToLocation state /////////////////////////////////////////////////////////
  class GoToLocation : public BehaviorStateT<GroupAttackBehavior, GoToLocation>
  {
  public:
    GoToLocation(GroupAttackBehavior& behavior);
    void onEnter();

    void onExit() {}

    void onUnitAdded(BWAPI::Unit* unit);

    void orderUnit(BWAPI::Unit* unit, BWAPI::Unit* closest);

    void onTransitionTimer();
    typedef SCHEDULER_EVENT_HANDLER(GoToLocation, onTransitionTimer) TransitionTimerEvent;
    void onOrderTimer();
    typedef SCHEDULER_EVENT_HANDLER(GoToLocation, onOrderTimer) OrderTimerEvent;

    typedef boost::tuple<
          TransitionTimerEvent&,
          OrderTimerEvent&
          > Events;
		Events m_events;
    
		typedef boost::tuple<
          Transition<GoToLocation, GroupIsAttacking, NoTransitionAction, Engaging>&,
          Transition<GoToLocation, GroupReachedLocation<PersistentUnitGroup>, NoTransitionAction, Terminate>&
		  > Transitions;
		Transitions m_transitions;

  
  private:
    TransitionTimerEvent m_transitionTimerEvent;
    OrderTimerEvent m_orderTimerEvent;
    // TODO: a group of High Templars (which cannot attack automatically) would always be stuck in GoToLocation
		Transition<GoToLocation, GroupIsAttacking, NoTransitionAction, Engaging> m_transitionToEngaging;
    Transition<GoToLocation, GroupReachedLocation<PersistentUnitGroup>, NoTransitionAction, Terminate> m_transitionToTerminate;
  };

  // Engaging state ////////////////////////////////////////////////////////////
  class Engaging : public BehaviorStateT<GroupAttackBehavior, Engaging>
  {
  public:
    Engaging(GroupAttackBehavior& behavior);
    void onEnter();
    void onExit() {}
    
    void onTimer();

    void onUnitAdded(BWAPI::Unit* unit);

  private:
    bool selectTarget();

    void onSubBehaviorTerminated(void* data);

    typedef SCHEDULER_EVENT_HANDLER(Engaging, onTimer) TimerEvent;
    TimerEvent m_engagingTimerEvent;

    EVENT_HANDLER(Engaging, onSubBehaviorTerminated) m_onSubBehaviorTerminated;
    Transition<Engaging, NoCondition, NoTransitionAction, GoToLocation> m_transitionToGoTo;

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

    typedef boost::tuple<
          Transition<Engaging, NoCondition, NoTransitionAction, GoToLocation>& 
      > Transitions;
    const Transitions m_transitions;
  };

  // Terminate state ////////////////////////////////////////////////////////////

public:
  GroupAttackBehavior(const Process* parent, unsigned int initialPriority, const PersistentUnitGroup* group, Location* location);
  
  typedef boost::tuple<
    GoToLocation&,
    Engaging&
  > States;
  const States m_states;

protected:
  friend class GoToLocation;
  friend class Engaging;
  friend class Terminate;

  virtual void _output(std::ostream& out) const
  {
    out << "GroupAttackBehavior(" << *getGroup() << ")";
  }

  virtual void onStart();
  virtual void onEnd();

  virtual void onUnitAddedToGroup(void* data, BWAPI::Unit* unit);
  virtual void onUnitRemovedFromGroup(void* data, BWAPI::Unit* unit);
  void onUnitMorph(void*, BWAPI::Unit* unit, BWAPI::UnitType previousType);
  BWAPI::Unit* getUnitToFollow(BWAPI::Unit* unit) const;
  BWAPI::Unit* getUnitToFollow() const;
  void manageUnits();
  UnitAttackBehavior* instantiateUnitAttackBehavior(BWAPI::Unit* unit) const;

  // Member variables //////////////////////////////////////////////////////////
  EVENT_HANDLER2(GroupAttackBehavior, onUnitMorph, BWAPI::Unit*, BWAPI::UnitType) m_unitMorphEventHandler;
  Event<boost::tuple<BWAPI::Unit*, BWAPI::UnitType>>::SubscriberID m_unitMorphID;
  const AutoPtr<Location> m_location;
  PersistentUnitGroup m_observerGroup;
  PersistentUnitGroup m_highTemplars;
  stdext::hash_map<BWAPI::Unit*, BehaviorWrapper> m_subBehaviors;

  // States
  Engaging m_engagingState;
  GoToLocation m_goToState;
  Terminate m_terminateState;

};