// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/obs_ctrl/os_sm.h 3768 2021-11-12 22:56:54Z depa $

#pragma once

//#include <Nice/Log.h>

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

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/lock_guard.hpp>

#include "../util/logger.h"

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_DECLARE_ATTRIBUTE(boost::shared_ptr<boost::mutex>, mut);
BOOST_MSM_EUML_ATTRIBUTES((attributes_ << initDone), init_attributes);
// BOOST_MSM_EUML_ATTRIBUTES((attributes_ << initDone << mut), 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(is_setup_done);
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);
        U6_LLOG(__FUNCTION__ << " ~ os_sm.h : SM: entering: Init");
        U4_LLOG("os_sm.h : ------ testAttr = " << state.get_attribute(testAttr));
        U8_LLOG("os_sm.h : ------ initDone = " << state.get_attribute(initDone));
    }
};

BOOST_MSM_EUML_ACTION(Init_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const &evt, FSM &fsm, STATE &state)
    {
        U6_LLOG("os_sm.h : SM leaving SpInit");
        state_(initDone) = True_();
        evt.get_attribute(initDone) = false;
        U8_LLOG("");

        // bool b = event_(initDone);
//      U4_LLOG("os_sm.h : " << "------ initDone = " << state_(initDone));
    }
};

BOOST_MSM_EUML_ACTION(Standby_Entry)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:entering: Standby");
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(Standby_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const &evt, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving: Standby " << evt.get_attribute(stanbyDone));
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(Error_Entry)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:entering: Error");
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(Error_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving: Error");
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(ready_Entry)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:entering : ready");
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(ready_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving : ready");
        U8_LLOG("");
    }
};

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);
        U6_LLOG("os_sm.h : SM:entering : busy");
        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(busy_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const &evt, FSM &fsm, STATE &state)
    {
        U6_LLOG("os_sm.h : SM:leaving : busy");
        U8_LLOG("");
    }
};

// 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&)
    {
        U6_LLOG("os_sm.h : SM:entering : OnlineFsm::PseudoEntryOnline" << evt.get_attribute(stanbyDone));
        U8_LLOG("");
    }
};

// Pseudo exit point to online
BOOST_MSM_EUML_ACTION(PseudoEntryOnline_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving : OnlineFsm::PseudoEntryOnline");
        U8_LLOG("");
    }
};

// Pseudo entry point to standby
BOOST_MSM_EUML_ACTION(PseudoExitOnline1_Entry)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:entering: OnlineFsm::PseudoExitOnline1");
        U8_LLOG("");
    }
};

// Pseudo exit point to standby
BOOST_MSM_EUML_ACTION(PseudoExitOnline1_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving : OnlineFsm::PseudoExitOnline1 ");
        U8_LLOG("");
    }
};

// Pseudo exit point to Error
BOOST_MSM_EUML_ACTION(PseudoExitOnline2_Entry)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:entering: OnlineFsm::PseudoExitOnline2");
        U8_LLOG("");
    }
};
BOOST_MSM_EUML_ACTION(PseudoExitOnline2_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving : OnlineFsm::PseudoExitOnline2");
        U8_LLOG("");
    }
};

// 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&)
    {
        U6_LLOG("os_sm.h : SM:entering: OnlineFsm");
        U8_LLOG("");
    }
};
BOOST_MSM_EUML_ACTION(OnlineFsm_Exit)
{
    template<class Event, class FSM, class STATE>
    void operator()(Event const&, FSM&, STATE&)
    {
        U6_LLOG("os_sm.h : SM:leaving : OnlineFsm");
        U8_LLOG("");
    }
};

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

BOOST_MSM_EUML_ACTION(set_running_devs)
{
    template<class Event, class FSM, class SourceState, class TargetState>
    void operator()(Event const &evt, FSM &fsm, SourceState &srcState, TargetState &trgState)
    {
        U6_LLOG("os_sm.h : set_running_dev to " << trgState.get_attribute(runningDevs));
        trgState.get_attribute(runningDevs) += evt.get_attribute(runningDevs);

        U8_LLOG("");
    }
};

BOOST_MSM_EUML_ACTION(decrease_running_devs)
{
    template<class Event, class FSM, class SourceState, class TargetState>
    void operator()(Event const &evt, FSM &fsm, SourceState &srcState, TargetState &trgState)
    {
        U6_LLOG(__FUNCTION__ << " ~ os_sm.h : decrease_running_devs");
        if (srcState.get_attribute(runningDevs) > 0)
        {
            --trgState.get_attribute(runningDevs);
            U7_LLOG("os_sm.h : SM: N Running devs = " << trgState.get_attribute(runningDevs));
            // if (trgState.get_attribute(runningDevs) == 0)
            //   fsm.process_event(busy_ready);
        }
        else
        {
            U7_LLOG("os_sm.h : No running devs!");
            // fsm.process_event(busy_ready);
        }
        U8_LLOG("");
    }
};

// BOOST_MSM_EUML_ACTION(set_for_standby)
// {
//   template <class FSM, class EVT, class SourceState, class TargetState>
//     void operator()(EVT const& evt, FSM& fsm, SourceState& srcState, TargetState& 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 TargetState>
//     void operator()(EVT const& evt, FSM&, SourceState&, TargetState&)
//   {
//     // 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&)
    {
        U6_LLOG("os_sm.h : SM : always_true");
        U8_LLOG("");
        return true;
    }
};

// ready_done
BOOST_MSM_EUML_ACTION(ready_done)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState&, TargetState&)
    {
        U6_LLOG("os_sm.h : SM : ready_done");
        U8_LLOG("");
        return evt.get_attribute(readyDone);
    }
};

// init_done
BOOST_MSM_EUML_ACTION(init_done)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState&, TargetState&)
    {
        //   evt.get_attribute(initDone) = false;
        U6_LLOG("os_sm.h : SM : init_done");
        U8_LLOG("");
        return evt.get_attribute(initDone);
    }
};

// standby_done
BOOST_MSM_EUML_ACTION(standby_done)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState&, TargetState&)
    {
        U6_LLOG("os_sm.h : SM : standbyt_done");
        U8_LLOG("");
        return evt.get_attribute(stanbyDone);
    }
};

// online_done
BOOST_MSM_EUML_ACTION(online_done)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState&, TargetState&)
    {
        U6_LLOG("os_sm.h : SM : online_done");
        U8_LLOG("");
        return evt.get_attribute(onlineDone);
    }
};

// setup_done
BOOST_MSM_EUML_ACTION(setup_done)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState &srcState, TargetState &trgState)
    {
        // if (srcState.get_attribute(runningDevs) == 0)
        //   evt.get_attribute(setupDone) = true;
        // else
        //   evt.get_attribute(setupDone) = false;
        bool guard = false;
        U6_LLOG("os_sm.h : SM : setup_done");
        if (srcState.get_attribute(runningDevs) == 0)
        {
            guard = true;
        }
        else
        {
            U4_LLOG("os_sm.h : SM : there are " << srcState.get_attribute(runningDevs) << " running devs");
        }
        U8_LLOG("");
        return guard;
    }
};

BOOST_MSM_EUML_ACTION(is_setup_done_act)
{
    template<class FSM, class EVT, class SourceState, class TargetState>
    bool operator()(EVT const &evt, FSM&, SourceState &srcState, TargetState &trgState)
    {
        bool ret = false;
        U6_LLOG("os_sm.h : SM : init_done");
        if (srcState.get_attribute(runningDevs) == 0)
        {
            ret = true;
        }
        U8_LLOG("");
        return ret;
    }
};

//BOOST_MSM_EUML_FUNCTION(Running_devs_, get_running_devs, running_devs_, void, void);
// -----------------------------------------------------------------
// Definig FLAGS and VISITORS
// ----------------------------------------------------------------
BOOST_MSM_EUML_FLAG(setupDoneFlag);

// struct SomeVisitor
// {
//   template <class T>
//   void visit_state(T* astate, int i)
//   {
//     std::cout << "visiting state:" << typeid(*astate).name()
//               << " with data:" << i << std::endl;
//   }
// };

// struct my_visitable_state
// {
//   // signature of the accept function
//   typedef msm::back::args<void,SomeVisitor&,int> accept_sig;

//   // we also want polymorphic states
//   virtual ~my_visitable_state() {}
//   // default implementation for states who do not need to be visited
//   void accept(SomeVisitor&,int) const {}
// };

// -----------------------------------------------------------------
// 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, attributes_ << no_attributes_, configure_ << no_configure_), ready);

// struct ready : public msm::front::state<my_visitable_state>
// {
//   template <class Event,class FSM>
//   void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;}
//   template <class Event,class FSM>
//   void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
//   void accept(SomeVisitor& vis,int i) const
//   {
//     vis.visit_state(this,i);
//   }
// };

// 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_running_devs, busy
        + is_setup_done / is_setup_done_act,
    //  +------------------------------------------------------------------------------+
    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); // this defined sub state machine using EUML front end

// 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;

}
