#pragma once
#include "../../../../Utils/Process/FSM/FSM.h"
#include "../../../../Utils/Functors.h"
#include "../../../../Scheduler/MP/MPScheduler.h"
// Some centralized header file inclusions...
#include "../Arbitrator.h"
#include "BehaviorState.h"

/**
 * @brief A behavior controls the execution of some aspect of the system. It corresponds to a finite 
 *        state machine producing actions and switching states according to the transitions activated
 *        by external events.
 */
class Behavior : public virtual FSM
{
public:
  /**
   * @brief Constructs a new behavior.
   * @param parent The parent of the new behavior.
   * @param initialPriority The initial priority of the new behavior.
   */
  Behavior(const Process* parent, unsigned int initialPriority)
    : FSM(parent)
    , Process(parent)
    , m_currentPriority(initialPriority)
  {
  }

  /**
   * @brief Behavior's destructor. All sub-behaviors should actually have been removed before destruction.
   */
  virtual ~Behavior() {}

  /**
   * @return The current priority of this behavior.
   */
  unsigned int getCurrentPriority() const
  {
    return m_currentPriority;
  }

protected:
  template <class FSMT, class DerivedState> friend class FSMStateT;
  template <class Behavior, class DerivedState> friend class BehaviorStateT;

  virtual void _output(std::ostream& out) const;

  /**
   * @brief Called when the behavior starts.
   */
  virtual void onStart()
  {
    SPAR_LOG(LogTypes.LAYER_1_DECISION, "Starting behavior: \"%s\"", getName().c_str());
    FSM::onStart();
  }

  /**
   * @brief Called when the behavior terminates.
   */
  virtual void onEnd()
  {
    SPAR_LOG(LogTypes.LAYER_1_DECISION, "Terminating behavior: \"%s\"", getName().c_str());
    FSM::onEnd();
  }

  template <class Function>
  Function forEachSubBehavior(Function function)
  {
    return forEachChildProcess(function);
  }

  template<class ResultType, class Operation, class Callback>
  void scheduleParallelTask(Operation operation, Callback callback, const std::string &name, unsigned int priority = 0)
  {
    class Wrapper
    {
      Behavior * m_behavior;
      Callback m_callback;
    public:    
      Wrapper(Behavior * behavior, Callback callback) : m_behavior(behavior), m_callback(callback) {}

      void operator()(const ResultType &returnValue)
      {
        try
        {
          m_callback(returnValue);
          m_behavior->removeUser();
        }
        catch(...)
        {
          m_behavior->removeUser();
          throw;
        }
      }
    };

    addUser();
    Spar->getMultiThreadScheduler()->schedule<ResultType>(operation, Wrapper(this, callback), name, priority);
  }

  //
  // Variables
  //

  /**
   * @brief The current priority of this behavior.
   */
  unsigned int m_currentPriority;

private:
  Behavior(const Behavior& other);
};

/**
 * @brief A behavior controls the execution of some aspect of the system. It corresponds to a finite 
 *        state machine producing actions and switching states according to the transitions activated
 *        by external events.
 * @tparam DerivedBehavior The most derived class of behavior. Used to allow static polymorphism.
 */
template <class DerivedBehavior>
class BehaviorT : public virtual Behavior, public FSMT<DerivedBehavior>
{
public:
  /**
   * @brief Constructs a new behavior.
   * @param parent The parent of the new behavior.
   * @param initialPriority The initial priority of the new behavior.
   */
  BehaviorT(const Process* parent, unsigned int initialPriority)
    : Behavior(parent, initialPriority)
    , FSMT(parent)
    , FSM(parent)
    , Process(parent)
  {
  }
};