#pragma once
#include "../../Event.h"
#include "../../SafeListAdaptor.h"
#include "../Process.h"
#include "FSMState.h"
#include <list>
#include <boost/iterator/transform_iterator.hpp>

// Should the initial state be given as a parameter to the FSM's constructor?

/**
 * @brief A Finite State Machine (FSM) executes actions upon being notified of events.
 */
class FSM : public virtual Process
{
public:
  /**
   * @brief Constructs a new FSM.
   * @param parent The parent of this new FSM.
   */
  FSM(const Process* parent)
    : Process(parent)
    , m_currentState(NULL)
  {
  }

  /**
   * @brief FSM's destructor. At this point all sub-FSMs should be removed.
   */
  virtual ~FSM()
  {
    assert(m_currentState == NULL);
  }

  /////////
  // GUI //
  /////////
  /**
   * @return All states present in this FSM.
   */
  virtual std::vector<const FSMState*> getAllStates() const = 0;
  /**
   * @return The current state this FSM is in.
   */
  const FSMState* getCurrentState() const
  {
    return m_currentState;
  }
  FSMState* getCurrentState()
  {
    return m_currentState;
  }

protected:
  friend class FSMState;
  template <class FSM, class DerivedState> friend class FSMStateT;

  /**
   * @brief Called when the FSM terminates. Make sure to terminate the current state if it is not NULL.
   */
  virtual void onEnd()
  {
    Process::onEnd();

    if (m_currentState != NULL)
      m_currentState->externalTerminate();

    assert(m_currentState == NULL);
  }

  /**
   * @brief Called from a state when the FSM is transitioning to another state.
   *        This is where cleanup of sub-processes occurs.
   */
  void onExitState()
  {
    m_currentState = NULL;
    forEachChildProcess(TerminateProcess());
  }

private:
  FSM(const FSM& other);

  /**
   * @brief Changes the current state of the FSM.
   * @param newState The new state of the FSM, possibly NULL.
   */
  void setCurrentState(FSMState* newState)
  {
    m_currentState = newState;
  }

  /**
   * @brief The current state this FSM is in, possibly NULL.
   */
  FSMState* m_currentState;
};

/**
 * @brief A Finite State Machine (FSM) executes actions upon being notified of events.
 * @tparam The most derived class of FSM. Used to allow static polymorphism.
 */
template <class DerivedFSM>
class FSMT : public virtual FSM
{
public:
  /**
   * @brief Constructs a new FSM.
   * @param parent The parent of this new FSM.
   */
  FSMT(const Process* parent)
    : FSM(parent)
    , Process(parent)
  {}

  /////////
  // GUI //
  /////////
  virtual std::vector<const FSMState*> getAllStates() const
  {
    std::vector<const FSMState*> states(boost::tuples::length<DerivedFSM::States>::value);
    StatesHelper<boost::tuples::length<typename DerivedFSM::States>::value>::copy(*this, states);
    
    return states;
  }

private:
  template <size_t i>
  struct StatesHelper
  {
    static void copy(const FSMT& fsm, std::vector<const FSMState*>& states)
    {
      states[i-1] = &boost::get<i-1>(static_cast<const DerivedFSM&>(fsm).m_states);
      StatesHelper<i-1>::copy(fsm, states);
    }
  };
  template <>
  struct StatesHelper<0>
  {
    static void copy(const FSMT&, std::vector<const FSMState*>&) {}
  };
};