#pragma once
#include "../Behavior.h"
#include "../BehaviorState.h"
#include "../PeriodicEventGenerator.h"
#include "../Actions/HealAction.h"
#include "../Actions/MoveAction.h"
#include "../Conditions/AlliesHealed.h"
#include <string>

/**
 * @brief The HealAllies behavior commands a medic unit to heal allies when they are wounded.
 */
class HealAllies : public UnitBehavior
{
protected:
  class Idle;
  class Healing;
  friend class Idle;
  friend class Healing;

  /**
   * @brief The Idle state is active when no ally is wounded.
   */
  class Idle : public BehaviorState<Idle>
  {
  public:
    Idle(HealAllies& behavior);
    void prepare();
    virtual std::string getName() const { return "Idle"; }

  protected:
    void onEnterImpl();
    void onExitImpl() {}
    friend class BehaviorState<Idle>;
    // Behavior
    HealAllies& m_behavior;

    // Variables

    // Actions
    MoveAction m_moveAction;

    // Transitions
    PeriodicEventGenerator<5> m_alliesWoundedEvent;
    Transition<Idle, AlliesWounded<PersistentUnitGroup>, NoTransitionAction, Healing> m_transitionToHealingState;
    Transition<Idle, NoCondition, NoTransitionAction, Idle> m_transitionToSelf;

    typedef boost::tuple<
        PeriodicEventGenerator<5>&
      > events;
    events m_events;
    
    typedef boost::tuple<
        Transition<Idle, AlliesWounded<PersistentUnitGroup>, NoTransitionAction, Healing>&,
        Transition<Idle, NoCondition, NoTransitionAction, Idle>&
      > transitions;
    transitions m_transitions;
  };

  /**
   * @brief The Healing state is active when an ally is wounded and being healed by the medic.
   */
  class Healing : public BehaviorState<Healing>
  {
  public:
    Healing(HealAllies& behavior);
    virtual std::string getName() const { return "Healing"; }

  protected:
    void onEnterImpl();
    void onExitImpl() {}
    friend class BehaviorState<Healing>;
    // Behavior
    HealAllies& m_behavior;

    // Variables

    // Actions
    HealAction m_healAction;

    // Transitions
    PeriodicEventGenerator<5> m_alliesHealedEvent;
    Transition<Healing, AlliesHealed<PersistentUnitGroup>, NoTransitionAction, Idle> m_transitionToIdleState;
    //Transition<Healing, AlliesWounded<PersistentUnitGroup>, NoTransitionAction, Healing> m_transitionToSelf;

    typedef boost::tuple<
        PeriodicEventGenerator<5>&
      > events;
    events m_events;
    
    typedef boost::tuple<
        Transition<Healing, AlliesHealed<PersistentUnitGroup>, NoTransitionAction, Idle>&
      > transitions;
    transitions m_transitions;
  };

public:
  HealAllies(unsigned int initialPriority, BWAPI::Unit* medic, PersistentUnitGroup* allies)
    : UnitBehavior(initialPriority, medic)
    , m_receptacle(*this)
    , m_idleState(*this)
    , m_healingState(*this)
    , m_allies(allies)
  {
    m_alliesID = m_allies->subscribeOnEmpty(&m_receptacle, NULL);
  }

  virtual ~HealAllies()
  {
    assert(m_allies != NULL);
    m_allies->unsubscribeOnEmpty(m_alliesID);
  }

  virtual std::string getName() const
  {
    return "HealAllies (" + getObjectDescription(m_unit) + " -> " + getObjectDescription(m_allies) + ")";
  }

  static const TerminationMode AlliesDead;

  void onAlliesDead(void* /*data*/)
  {
    m_allies->unsubscribeOnEmpty(m_alliesID);
    terminate(AlliesDead);
  }

protected:
  virtual void enterInitialState()
  {
    m_idleState.prepare();
    m_idleState.onEnter();
  }

  // Variables
  PersistentUnitGroup* const m_allies;
  RECEPTACLE(HealAllies, onAlliesDead) m_receptacle;
  PersistentUnitGroup::GroupEmptySubscriberID m_alliesID;

  // States
  Idle m_idleState;
  Healing m_healingState;
};