#ifndef FUNCTOR_SEQUENCER_H
#define FUNCTOR_SEQUENCER_H
  #include <stdint.h>
  #include <stddef.h>
  //
  // Functor Sequencer v1.0
  //
  // Sychov A., jul/2019
  //
  // The functor sequencer.
  // This module implements the model of abstract programming (behaviour) that 
  // implying a set of abstract states are switching sequently using so called
  // functors. The object that involves an user-defined context, user-defined
  // set of abstract states is called "sequencer". Each functor represents either 
  // the only user-defined function with user context, or a set of such functions.
  // The set (@fFSeqEntry_t) consist of the user-defined function with user 
  // context and two special user-defined functions - "enter" and "leave" - 
  // that are called on the state changes. The state can be changed in two ways: 
  // internally by the function itself and externally by calling module function 
  // "Startup". The first way is the main and the only correct method of chaning
  // state of the running sequencer. The other method is should only used to start
  // up the sequencer with entering the specified state after constructing of a 
  // sequencer object. To switch of running sequencer the "Shutdown" method is used.
  // The method "Dispatch" provides the sequencer operation and must be called
  // externally to make the sequencer to perform it's function.
  // The "entry" and "exit" functions are called automatically by the sequencer
  // if the functions are enabled by setting @FSEQ_ENTRY_EXIT_SUPPORT to 1.
  // The "entry" function has a prototype @fSeqEnter_t.
  // The "leave" function has a prototype @fSeqLeave_t.
  #define FSEQ_ENTRY_EXIT_SUPPORT   1

  // @FSEQ_ALT_INHERIT_METHOD_SUPPORT
  // Each state may have it's own private context. This macro chooses the method
  // of accessing to the private context.
  // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is zero (Method #0): 
  //    the state function body can get access to the state private context using casting 
  //    the pointer @fFSeqEntry_t* to the pointer to container:
  //      const struct { fFSeqEntry_t state; void * ctx_ref_ven; } stateContainer;
  //    This method uses the @ctx_ref_ven constant pointer a.k.a veneer, that refers
  //    to the actual private context:
  //     const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this, 
  //                                         tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  //     {
  //        // get access to the @ctx_ref_ven field from @stateContainer structure
  //        const void * const_ctx_ref_ven = (const void*)(&this[1]); 
  //        ...
  //        void * private_ctx = (void*)const_ctx_ref; // cast the pointer
  //        ... use @private_ctx
  //     }
  //     Advantage: the @ctx_ref_ven is constant and refers to the private context directly,
  //                easy to manage the private context for each state, easy code support,
  //                you do not need to keep the state pointer into the RAM.
  //     Disadvantage: the same, the @ctx_ref_ven is constant, and if you need multiple instances
  //                of the the same state, you need to create multiply copies of the state entry,
  //                due to you need to change @ctx_ref_ven field.  
  //    
  // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is non-zero (Method #1): 
  //     Alternative method. The state function body can get access to the state private context 
  //     the same way, using casting the pointer @fFSeqEntry_t* to the pointer to container, but
  //     the container must be declared in the RAM:
  //     typedef struct { ... } tPrivateContextData_t;
  //     struct { fFSeqEntry_t state; tPrivateContextData_t data; } stateContainer;
  //     // ...
  //     const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this, 
  //                                         tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  //     {
  //        // get access to the actual private context @private_ctx (field from @stateContainer structure)
  //        tPrivateContextData_t * private_ctx_data = (void*)(&this[1]); 
  //        ...  
  //        ... use @private_ctx_data
  //     }
  //
  //     Advantage: since the @stateContainer is located in the RAM, you don't need a veneer,
  //                the private fields are located directly "after" the state entry in the memory,
  //                you can easily to add new instances of the state by creating new container.
  //     Disadvantage: each container for state instance must contain the state entry pointer, that
  //                is located in the RAM, also the code support is more complicated due to
  //                you need to link the dynamic container and the static state entry.
  //
  //  Internally, the method #0 is quite differ than the method #1, because the state entry actually
  //  does not contain any state functions in the method #1 but only the virtual table pointer,
  //  and in the method #0 the state entry consist of the state functions itself. It is a important
  //  point to understand how it works and how to inherit and embedd the state entry in your application.
  //  But if you do not need a private context, it is does not matter which method you choose due to
  //  the interface of the module does not depend on this issue.
  #define FSEQ_ALT_INHERIT_METHOD_SUPPORT   1

  // User is allowed to pass some user-dependent context via special parameter
  // of type "tFSeqCtx_t". This argument must be set on constructing the 
  // sequencer object during the constructing function call (@fSeqConstruct).
  typedef void * tFSeqCtx_t;  // user context
  
  // @fFSeqEntry_t (forward declaration)
  // The set of user-defined functions (see below)
  struct fFSeqEntry_t;

  typedef const struct fFSeqEntry_t * ( * fSeq_t)( const struct fFSeqEntry_t * this, 
                                          tFSeqCtx_t ctx,
                                          const struct fFSeqEntry_t * * pnext );

  #if FSEQ_ENTRY_EXIT_SUPPORT
  typedef void ( * fSeqEnter_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  typedef void ( * fSeqLeave_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  #endif


  #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
  // The user-defined state methods.
  // Is only used when FSEQ_ALT_INHERIT_METHOD_SUPPORT is enabled.
  // The structure represents the state virtual table methods.
  typedef struct fFSeqVTable_t
  {
      fSeq_t f;                      // function body
      #if FSEQ_ENTRY_EXIT_SUPPORT    // 
      fSeqEnter_t enter;             // entry-function
      fSeqLeave_t leave;             // exit-function
      #endif
  }
  fFSeqVTable_t;
  #endif

  // @fFSeqEntry_t
  // The user-defined state entry.
  // Can either contain the user-defined methods itself, or
  // contain only reference to the methods table (see @FSEQ_ALT_INHERIT_METHOD_SUPPORT)
  // User can use "inheritance" method to declare some
  // additional state-dependent context, see the example below.
  typedef struct fFSeqEntry_t
  {
      #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
          const fFSeqVTable_t * vtbl;    // state virtual methods table reference
      #else
          fSeq_t f;                      // function body
          #if FSEQ_ENTRY_EXIT_SUPPORT    // 
          fSeqEnter_t enter;             // entry-function
          fSeqLeave_t leave;             // exit-function
          #endif
      #endif
  }
  fFSeqEntry_t;

  #if FSEQ_ALT_INHERIT_METHOD_SUPPORT == 0
  // @DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT
  // Declares user-defined state for sequencer
  // @name - the name of the state;  
  // @ctx_var - user context variable (structure)
  // @... - va_arg arguments: state functions list (body, entry, exit)
  #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
       const struct {\
         fFSeqEntry_t stateEntry;\
         void * ctx_ref;\
       } statename = {\
         .stateEntry = { __VA_ARGS__ },\
         .ctx_ref = &ctx_var\
       };
  #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
       struct {
  #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
       } ctx_var;
  #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
       } ctx_var = {
  #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
       };
  #else
  #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
       const fFSeqVTable_t statename##_vtbl = { __VA_ARGS__ };

  #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
       extern const fFSeqVTable_t statename##_vtbl;\
       const struct {\
          fFSeqEntry_t stateEntry;\

  #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
       } ctx_var = { \
         .stateEntry = { .vtbl = &statename##_vtbl },\
       };
  #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
       } ctx_var = { \
         .stateEntry = { .vtbl = &statename##_vtbl },
  #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
       };
  #endif
  /* 
  // ------------------------------------------------------------------------------------------------
  // Inheritance example (Method #0, see @FSEQ_ALT_INHERIT_METHOD_SUPPORT):
     xFSeqObj_t seq; // sequencer
  // ...
  // first state vtable method prototypes:
     const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
     void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t );
     void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t );
  // ...
     struct
     {
        int publicVar;
     }
     GlobalContext; // no comments
  // ...
     typedef struct
     { 
        int privateVar;
     }
     tFirstStatePrivateContext; 
    
     tFirstStatePrivateContext firstStatePrivateDynamicContext; // the dynamic private context of first state (RAM)
  // ...
  // The state user-structure, incuding the state entry itself (firstState) and the const pointer to the another object.
     const struct {
       fFSeqEntry_t firstState;
       void * ctx_ref;
     } firstStateExtended = { 
       .firstState = { firstStateFunction, firstStateEnter, firstStateLeave },
       .ctx_ref = &firstStatePrivateDynamicContext  // link the const pointer with the dynamic object
     };
  // ...
     void foo() // some user function
     {
        fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context
     }
  // ...
     // @firstStateFunction
     // firstState function body     
     const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
     {            
         const void * const_ctx_ref = (const void*)(&this[1]); // get access to the @ctx_ref field from @firstStateExtended structure
  // ...
         tFirstStatePrivateContext * private_ctx = (void*)const_ctx_ref; // cast the pointer 
  // ... 
         private_ctx->privateVar = 1; // use private context
     }
  // ------------------------------------------------------------------------------------------------
  */

  /* 
  // ------------------------------------------------------------------------------------------------
  // Inheritance example (Method #1, see @FSEQ_ALT_INHERIT_METHOD_SUPPORT):
     xFSeqObj_t seq; // sequencer
  // ...
  // first state vtable method prototypes:
     const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
     void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t );
     void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t );
  // ...
     struct
     {
        int publicVar;
     }
     GlobalContext; // no comments
  // ...
     typedef struct
     { 
        int privateVar;
     }
     tFirstStatePrivateContext;
  // ...
     typedef struct
     { 
        fFSeqEntry_t firstState;
        tFirstStatePrivateContext privateContext;
     }
     tFirstStateAndPrivateContext; 

   // the structure of state methods:
     const fFSeqVTable_t firstStateVTable = { firstStateFunction, firstStateEnter, firstStateLeave };     

   // The state user-structure, incuding the state entry, and the private context itself:
     tFirstStateAndPrivateContext firstStateExtended = { // the dynamic private context of first state (RAM)
         .firstState.vtbl = &firstStateVTable,         
         .privateContext = { .privateVar = 0, // some values...
         }       
     };
  // ...  
  // ...
     void foo() // some user function
     {
        fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context
     }
  // ...
     // @firstStateFunction
     // firstState function body     
     const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
     {            
         tFirstStatePrivateContext * private_ctx = (tFirstStatePrivateContext*)(&this[1]); // get access to the private context  
  // ... 
         private_ctx->privateVar = 1; // use private context
     }
  // ------------------------------------------------------------------------------------------------
  */

  // @xFSeqObj_t
  // The sequencer object, contains some private fields
  // that must not be accessed by user. 
  typedef struct
  {
      const void * private[2];
  }
  xFSeqObj_t;  

  // @fSeqConstruct
  // Construct the sequencer object, but not run it.
  // @fSeqObj - the object to construct
  // @first - the main state of the object, can not be NULL
  // @ctx - user-defined context, can be NULL
  void fSeqConstruct( xFSeqObj_t * fSeqObj, const fFSeqEntry_t * first, tFSeqCtx_t ctx );

  // @fSeqStartup
  // Starts the constructed sequencer.
  // @fSeqObj - the sequencer object to start
  // @first - the state to start from, can be NULL
  // If @first is NULL, the value @first from the @fSeqConstruct() call will be used. 
  void fSeqStartup( xFSeqObj_t * fSeqObj, const fFSeqEntry_t * first );

  // @fSeqDispatch
  // Provides the sequencer work
  // Must be called to make the sequencer call the state functions
  // @fSeqObj - the sequencer object to operate with 
  void fSeqDispatch( xFSeqObj_t * fSeqObj );

  // @fSeqShutdown
  // Shutdowns the running sequencer
  // @fSeqObj - the sequencer object to shutdown 
  void fSeqShutdown( xFSeqObj_t * fSeqObj );


  /* Macro using example:
  const fFSeqEntry_t * state1Function( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
  void state1Enter( const fFSeqEntry_t *, tFSeqCtx_t );
  void state1Leave( const fFSeqEntry_t *, tFSeqCtx_t );

  DECLARE_FSEQ_STATE_CONTEXT_BEGIN(seqState1, seqState1Context)
    struct {
       int state1_var;
    };
  DECLARE_FSEQ_STATE_CONTEXT_END(seqState1, seqState1Context)
  DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT( seqState1, seqState1Context, state1Function, state1Enter, state1Leave );
  */
#endif