//$Id: os_sm.h 262 2017-10-25 16:15:38Z depa $

#ifndef _OS_SM_
#define _OS_SM_

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/euml/euml.hpp>


using namespace boost::msm::front::euml;
namespace msm = boost::msm;
namespace mpl = boost::mpl;

// EVENT[GUARD] / ACTION
// - EVENT: trigger transition between two states. To an EVENT, ACTION and GUARD can be attached
// - GUARD: is executed before the transition. Can prevent transtion from firing. It is a
//          boolean opertation.
// - ACTION: is executed when the transition fires

namespace osfsm
{
  //
  /* EVENTS
   * -- with attributes --
   * init_standby
   * ready_standby
   * standby_online
   * busy_ready
   * 
   * go_offline
   * online_standby
   * setup_failed
   * ready_busy
   */

  // -----------------------------------------------------------------
  // Declearing attributes
  // -----------------------------------------------------------------

  // initDone
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(bool, initDone);
  BOOST_MSM_EUML_ATTRIBUTES((attributes_ << initDone), init_attributes);
  // readDone
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(bool, readyDone);
  BOOST_MSM_EUML_ATTRIBUTES((attributes_ << readyDone), ready_attributes);
  // standbyDone
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(bool, stanbyDone);
  // onlineDone
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(bool, onlineDone);
  BOOST_MSM_EUML_ATTRIBUTES((attributes_ << stanbyDone << onlineDone), standby_attributes);
  // setupDone
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(bool, setupDone);
  // BOOST_MSM_EUML_ATTRIBUTES((attributes_ << setupDone), busy_attributes);
  // runningDevs
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, runningDevs);
  BOOST_MSM_EUML_ATTRIBUTES((attributes_ << runningDevs), setup_attributes);
  // test
  BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, testAttr);
  // BOOST_MSM_EUML_ATTRIBUTES((attributes_ << testAttrib), test_attributes);

  // -----------------------------------------------------------------
  // Definig ENVENTS with attributes
  // -----------------------------------------------------------------

  // init_standby
  BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(init_standby, init_attributes);
  // ready_standby
  BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(ready_standby, ready_attributes);
  // standby_online
  BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(standby_online, standby_attributes);
  // busy_ready
  // BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(busy_ready, busy_attributes);
  // ready_busy
  BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(ready_busy, setup_attributes);

  // -----------------------------------------------------------------
  // Defining EVENTS without attributes
  // -----------------------------------------------------------------

  // dev_in_position
  BOOST_MSM_EUML_EVENT(dev_in_position);
  BOOST_MSM_EUML_EVENT(busy_ready);
  // go_offline
  BOOST_MSM_EUML_EVENT(go_offline);

  // -----------------------------------------------------------------
  // Definig EVENTS for transions from substates to exit/entry points
  // -----------------------------------------------------------------

  // online_standby
  struct online_standby_impl : euml_event<online_standby_impl>
  {
    online_standby_impl(){}
    template <class Event>
    online_standby_impl(Event const&){}
  };
  online_standby_impl const online_standby;

  // setup_failed
  struct setup_failed_impl : euml_event<setup_failed_impl>
  {
    setup_failed_impl(){}
    template <class Event>
    setup_failed_impl(Event const&){}
  };
  setup_failed_impl const setup_failed;


  // -----------------------------------------------------------------
  // Defining Entry and Exit points ACTIONS
  // -----------------------------------------------------------------

  BOOST_MSM_EUML_ACTION(Init_Entry)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const& evt, FSM& fsm, STATE& state)
    {
      ++state.get_attribute(testAttr);
      std::cout << "-->SM: entering: Init" << std::endl;
      std::cout << "------ testAttr = " << state.get_attribute(testAttr);
      std::cout << std::endl;
      std::cout << "------ initDone = " << state.get_attribute(initDone);
      std::cout << std::endl << "--------------------" << std::endl;
      // std::cout << "------ initDone = " << state_(initDone)
      //           << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(Init_Exit)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const& evt, FSM& fsm, STATE& state)
    {
      state_(initDone) = True_();
      evt.get_attribute(initDone) = false;

      // bool b = event_(initDone);
      std::cout << "-->SM:leaving: SpInit" << std::endl
                << "------ initDone = " << state_(initDone);
      std::cout << std::endl;
      std::cout << "--------------------" << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(Standby_Entry)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const&, FSM&, STATE& )
    {
      std::cout << "-->SM:entering: Standby" << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(Standby_Exit)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const& evt, FSM&, STATE& )
    {
      std::cout << "-->SM:leaving: Standby " << evt.get_attribute(stanbyDone);
      std::cout << std::endl;
    }
  };


  BOOST_MSM_EUML_ACTION(Error_Entry)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const&, FSM&, STATE& )
    {
      std::cout << "-->SM:entering: Error" << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(Error_Exit)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const&, FSM&, STATE& )
    {
      std::cout << "-->SM:leaving: Error" << std::endl;
    }
  };


  BOOST_MSM_EUML_ACTION(ready_Entry)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const&, FSM&, STATE& )
    {
      std::cout << "-->SM:entering: ready" << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(ready_Exit)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const&, FSM&, STATE& )
    {
      std::cout << "-->SM:leaving: ready" << std::endl;
    }
  };


  BOOST_MSM_EUML_ACTION(busy_Entry)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const& evt, FSM& fsm, STATE& state)
    {
      //state.get_attribute(runningDevs);
      // std::cout << evt.get_attribute(runningDevs);
      // state.get_attribute(runningDevs) = event_(runningDevs);
      std::cout << "--> SM:entering: busy. "  << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(busy_Exit)
  {
    template<class Event,class FSM,class STATE>
      void operator()(Event const& evt, FSM& fsm, STATE& state)
    {
      std::cout << "-->SM:leaving: busy" << std::endl;
    }
  };

  // Pseudo entry point to online
  BOOST_MSM_EUML_ACTION(PseudoEntryOnline_Entry)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const& evt,FSM&,STATE& )
    {

      std::cout << "-->SM:entering: OnlineFsm::PseudoEntryOnline" << evt.get_attribute(stanbyDone);
      std::cout << std::endl;
    }
  };

  // Pseudo exit point to online
  BOOST_MSM_EUML_ACTION(PseudoEntryOnline_Exit)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:leaving: OnlineFsm::PseudoEntryOnline" << std::endl;
    }
  };


  // Pseudo entry point to standby
  BOOST_MSM_EUML_ACTION(PseudoExitOnline1_Entry)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:entering: OnlineFsm::PseudoExitOnline1" << std::endl;
    }
  };

  // Pseudo exit point to standby
  BOOST_MSM_EUML_ACTION(PseudoExitOnline1_Exit)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:leaving: OnlineFsm::PseudoExitOnline1" << std::endl;
    }
  };

  // Pseudo exit point to Error
  BOOST_MSM_EUML_ACTION(PseudoExitOnline2_Entry)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:entering: OnlineFsm::PseudoExitOnline2" << std::endl;
    }
  };
  BOOST_MSM_EUML_ACTION(PseudoExitOnline2_Exit)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:leaving: OnlineFsm::PseudoExitOnline2" << std::endl;
    }
  };


  // Online sub state machine ENTRY and EXIT points

  BOOST_MSM_EUML_ACTION(OnlineFsm_Entry)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:entering: OnlineFsm" << std::endl;
    }
  };
  BOOST_MSM_EUML_ACTION(OnlineFsm_Exit)
  {
    template <class Event,class FSM,class STATE>
      void operator()(Event const&,FSM&,STATE& )
    {
      std::cout << "-->SM:leaving: OnlineFsm" << std::endl;
    }
  };

  // -----------------------------------------------------------------
  // Defining ACTIONS ()functor returning void);
  // -----------------------------------------------------------------

  BOOST_MSM_EUML_ACTION(set_running_devs)
  {
    template<class Event, class FSM, class SourceState, class TargetSate>
      void operator()(Event const& evt, FSM& fsm, SourceState& srcState, TargetSate& trgState)
    {
      trgState.get_attribute(runningDevs) += evt.get_attribute(runningDevs);

      std::cout << "set_running_dev to " << trgState.get_attribute(runningDevs);
      std::cout << std::endl;
    }
  };

  BOOST_MSM_EUML_ACTION(decrease_runnig_devs)
  {
    template<class Event,class FSM,class SourceState, class TargetSate>
      void operator()(Event const& evt, FSM& fsm, SourceState& srcState, TargetSate& trgState)
    {
      if (srcState.get_attribute(runningDevs) > 0)
        {
          --trgState.get_attribute(runningDevs);
          std::cout << "-->SM: N Running devs = " << trgState.get_attribute(runningDevs);
          std::cout << std::endl;
          if (trgState.get_attribute(runningDevs) == 0)
            fsm.process_event(busy_ready);
        }
      else
        {
          std::cout << "-->SM: No running devs!" << std::endl;
          fsm.process_event(busy_ready);
        }
    }
  };

  // BOOST_MSM_EUML_ACTION(set_for_standby)
  // {
  //   template <class FSM, class EVT, class SourceState, class TargetSate>
  //     void operator()(EVT const& evt, FSM& fsm, SourceState& srcState, TargetSate& trgState)
  //   {
  //     // set subsystems standby
  //     std::cout << "-->SM:Set subsystems for STANDBY" << std::endl;
  //   }
  // };

  // BOOST_MSM_EUML_ACTION(set_for_online)
  // {
  //   template <class FSM, class EVT, class SourceState, class TargetSate>
  //     void operator()(EVT const& evt, FSM&, SourceState&, TargetSate&)
  //   {
  //     // set subsystems online
  //     std::cout << "-->SM:Set subsystems for ONLINE" << std::endl;
  //   }
  // };



  // -----------------------------------------------------------------
  // Defining  GUARDS (functor returnig bool);
  // -----------------------------------------------------------------

  // always_true
  BOOST_MSM_EUML_ACTION(always_true)
  {
    template <class FSM,class EVT,class SourceState,class TargetState>
      bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
    {
      std::cout << "-->SM:always_true" << std::endl;
      return true;
    }
  };

  // ready_done
  BOOST_MSM_EUML_ACTION(ready_done)
  {
    template <class FSM, class EVT, class SourceState, class TargetSate>
      bool operator()(EVT const& evt, FSM&, SourceState&, TargetSate&)
    {
      return evt.get_attribute(readyDone);
    }
  };

  // init_done
  BOOST_MSM_EUML_ACTION(init_done)
  {
    template <class FSM, class EVT, class SourceState, class TargetSate>
      bool operator()(EVT const& evt, FSM&, SourceState&, TargetSate&)
    {
      //   evt.get_attribute(initDone) = false;
      return evt.get_attribute(initDone);
    }
  };

  // standby_done
  BOOST_MSM_EUML_ACTION(standby_done)
  {
    template <class FSM, class EVT, class SourceState, class TargetSate>
      bool operator()(EVT const& evt, FSM&, SourceState&, TargetSate&)
    {
      return evt.get_attribute(stanbyDone);
    }
  };

  // online_done
  BOOST_MSM_EUML_ACTION(online_done)
  {
    template <class FSM, class EVT, class SourceState, class TargetSate>
      bool operator()(EVT const& evt, FSM&, SourceState&, TargetSate&)
    {
      return evt.get_attribute(onlineDone);
    }
  };

  // setup_done
  BOOST_MSM_EUML_ACTION(setup_done)
  {
    template <class FSM, class EVT, class SourceState, class TargetSate>
      bool operator()(EVT const& evt, FSM&, SourceState& srcState, TargetSate& trgState)
    {
      // if (srcState.get_attribute(runningDevs) == 0)
      //   evt.get_attribute(setupDone) = true;
      // else
      //   evt.get_attribute(setupDone) = false;
      bool guard = false;
      if (srcState.get_attribute(runningDevs) == 0)
        {
          guard = true;
        }
      else
        {
          std::cout << "--->SM: there are " << srcState.get_attribute(runningDevs); 
          std::cout << " running devs" << std::endl;
        }
      return guard;
    }
  };

  // -----------------------------------------------------------------
  // Definig STATES
  // -----------------------------------------------------------------

  // Init
  BOOST_MSM_EUML_STATE((Init_Entry, Init_Exit,
                        attributes_ << initDone << testAttr), Init);
  // Standby
  BOOST_MSM_EUML_STATE((Standby_Entry, Standby_Exit,
                        attributes_ << stanbyDone), Standby);
  // Error
  BOOST_MSM_EUML_STATE((Error_Entry, Error_Exit), Error);
  // ready
  BOOST_MSM_EUML_STATE((ready_Entry, ready_Exit), ready);
  // busy
  BOOST_MSM_EUML_STATE((busy_Entry, busy_Exit,
                        attributes_ << runningDevs), busy);


  // -----------------------------------------------------------------
  // Defining ENTRY and EXIT states for Online sub state machine
  // -----------------------------------------------------------------

  // PseudoEntryOnline
  BOOST_MSM_EUML_ENTRY_STATE(0,// region index to be entered
                             ( PseudoEntryOnline_Entry,PseudoEntryOnline_Exit),
                             PseudoEntryOnline);
  // PseudoExitOnline1
  BOOST_MSM_EUML_EXIT_STATE((online_standby, // propagated event
                             PseudoExitOnline1_Entry,PseudoExitOnline1_Exit),PseudoExitOnline1);
  // PseudoExitOnline2
  BOOST_MSM_EUML_EXIT_STATE((setup_failed, // propagated event
                             PseudoExitOnline2_Entry,PseudoExitOnline2_Exit),PseudoExitOnline2);

  // -------------------------------------------------------------------
  // Defining STATE MACHINES
  // -----------------------------------------------------------------

  // Online sub statemachine
  BOOST_MSM_EUML_TRANSITION_TABLE((
  ready                                 == PseudoEntryOnline   ,
  ready + ready_busy / set_running_devs == busy                ,
  //  +------------------------------------------------------+
  busy + busy_ready[setup_done]         == ready               ,
  busy + dev_in_position / decrease_runnig_devs                ,
  //  +------------------------------------------------------+
  PseudoExitOnline1 == ready + ready_standby[ready_done] ,
  PseudoExitOnline2 == busy + setup_failed
  //  +------------------------------------------------------+
  ), OnlineFsm_transition_table);

  BOOST_MSM_EUML_DECLARE_STATE_MACHINE((OnlineFsm_transition_table,
                                        init_ << ready,
                                        OnlineFsm_Entry,
                                        OnlineFsm_Exit
                                        ), OnlineFsm_def);

  // back-end
  typedef::msm::back::state_machine<OnlineFsm_def> Online_type;
  Online_type const OnlineFsm;

  // OS statemachine
  BOOST_MSM_EUML_TRANSITION_TABLE((
  Init    + init_standby[init_done]      == Standby                                 ,
  //  +----------------------------------------------------------------------------------+
  Standby + standby_online[standby_done] == entry_pt_(OnlineFsm, PseudoEntryOnline) ,
  //  +----------------------------------------------------------------------------------+
  Standby == exit_pt_(OnlineFsm, PseudoExitOnline1) + online_standby                ,
  //  +----------------------------------------------------------------------------------+
  exit_pt_(OnlineFsm, PseudoExitOnline2) + setup_failed == Error
  ), transition_table);


  BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table,
                                        init_ << Init,
                                        no_action, //Entry
                                        no_action  //Exit
                                        ), OSFsm_);

  // back-end
  typedef msm::back::state_machine<OSFsm_> OSFsm;

}

#endif // _OS_SM_
