#pragma once
#include "../../Component.h"
#include "../../Event.h"
#include "Transition.h"
#include <boost/tuple/tuple.hpp>

class FSM;

/**
 * @brief A FSM state represents a state in a finite state machine. Each state stores code to execute
 *        when it becomes active, and transitions associated to events to get out of that state.
 */
class FSMState : public virtual Component
{
public:
  /**
   * @brief Constructs a new FSM state (o rly?)
   */
  FSMState()
    //: m_stateExitEvent(*this)
  {
  }

  // SC 2012/08/15: commented out since it is never used at the moment
  //DECLARE_EVENT(OnStateExit);
  //OnStateExit& getStateExitEvent() { return m_stateExitEvent; }

protected:
  friend class FSM;
  /**
   * @brief This method is called when the FSM must be terminated due to an external event
   *        (for example a unit FSM may be destroyed when its associated unit dies).
   *        All registered events or dependent FSMs must be taken care of.
   */
  virtual void externalTerminate() = 0;

  //EVENT(OnStateExit) m_stateExitEvent;

private:
  FSMState& operator=(const FSMState&);
};


/**
 * @brief A FSM state represents a state in a finite state machine. Each state stores code to execute
 *        when it becomes active, and transitions associated to events to get out of that state.
 * @tparam FSM The type of the enclosing FSM.
 * @tparam DerivedState The most derived class of FSM state. Used to allow static polymorphism.
 */
template <class FSM, class DerivedState>
class FSMStateT : public FSMState
{
public:
  /**
   * @brief Constructs a new FSM state.
   * @param context The enclosing FSM.
   */
  FSMStateT(FSM &context)
    : m_context(context)
    , m_earliestNextTransitionFrame(0)
  {
  }

  /**
   * @brief Runs the (hopefully short) code associated with this state.
   */
  void enter()
  {
    m_context.setCurrentState(this);

    static_cast<DerivedState*>(this)->onEnter();

    if (m_context.isExecuting())
    {
      activateEvents();
    }
  }

  /**
   * @brief This method is called when a transition (or a termination) sends the finite state machine
   *        out of that state.
   * @note This method can be overriden in subclasses, but don't forget to call FSMStateT::exit()!
   */
  void exit()
  {
    assert(m_context.m_currentState != NULL);

    static_cast<DerivedState*>(this)->onExit();

    deactivateEvents();

    m_context.onExitState();

#ifdef _DEBUG_COMPONENT
    _verifyPendingSubscriptions("ERROR FSMStateT::exit(): Make sure to unsubscribe event handler \"%s\" from event \"%s\" when the FSM exits a state.");

    _verifyPendingTasks("ERROR FSMStateT::exit(): Make sure to deactivate task \"%s\" when the FSM exits a state.");
#endif

    //m_stateExitEvent.raise();
  }

  /**
   * @return The earliest next frame where a transition is allowed to take place.
   */
  size_t getEarliestNextTransitionFrame() const
  {
    return m_earliestNextTransitionFrame;
  }

  /**
   * @brief Sets the earliest next frame number where a transition is allowed to take place.
   * @param earliestAllowedFrameNumber The earliest next frame number where a transition is allowed to take place.
   */
  void setEarliestNextTransitionFrame(size_t earliestAllowedFrameNumber)
  {
    m_earliestNextTransitionFrame = earliestAllowedFrameNumber;
  }

protected:
  virtual void _output(std::ostream& out) const
  {
    out << m_context.getName() << "." << this->getName();
  }

  /**
   * @brief This method is called when the FSM must be terminated due to an external event.
   *        All registered events or dependent FSMs must be taken care of.
   */
  virtual void externalTerminate()
  {
    exit();
  }

  /**
   * @brief EventsHelper is used solely to loop through the compile-time array of events (meta-programming).
   */
  template <size_t size>
  struct EventsHelper
  {
    static void activate(const FSMStateT& state)
    {
      boost::get<size-1>(static_cast<const DerivedState&>(state).m_events).activate();
      EventsHelper<size-1>::activate(state);
    }
    static void deactivate(const FSMStateT& state)
    {
      boost::get<size-1>(static_cast<const DerivedState&>(state).m_events).deactivate();
      EventsHelper<size-1>::deactivate(state);
    }
  };
  /**
   * @brief EventsHelper base case.
   */
  template <>
  struct EventsHelper<0>
  {
    static void activate(const FSMStateT&) {}
    static void deactivate(const FSMStateT&) {}
  };

  /**
   * @brief Enables all transitions attached to this state.
   */
  void activateEvents()
  {
    EventsHelper<boost::tuples::length<DerivedState::Events>::value>::activate(*this);
  }

  /**
   * @brief Disables all transitions attached to this state.
   */
  void deactivateEvents()
  {
    EventsHelper<boost::tuples::length<DerivedState::Events>::value>::deactivate(*this);
  }

  // Problem: "Events m_events" and "Transitions m_transitions" cannot be stored in here as DerivedType is
  // not yet defined in FSMStateT<DerivedState> (so they must be defined in derived classes instead).

  /**
   * @brief The enclosing FSM storing data shared by all states.
   */
  FSM &m_context;

private:
  /**
   * @brief Indicates the earliest next frame where a transition is allowed to take place.
   *        This is used to prevent multiple transitions from occuring simultaneously (in the same frame); 
   *        this may happen if for example two Transitions are notified sequentially about the same event.
   */
  size_t m_earliestNextTransitionFrame;

  FSMStateT(const FSMStateT& other);
};
