#include "app/fseq/fseq.h"
#include "my_assert.h"
#include <stdbool.h>

// @sFSeqObj_t
// The sequencer private structure
typedef struct
{
    const fFSeqEntry_t * fEntry;   // current "function object" pointer
    tFSeqCtx_t ctx;                // user context
}
sFSeqObj_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 * fSeq, const fFSeqEntry_t * first, tFSeqCtx_t ctx )
{   
    my_assert( fSeq ); 
    my_assert( first );
    my_assert( first->vtbl );
    my_assert( first->vtbl->f );
    if( NULL != fSeq && NULL != first )
    {
        ((sFSeqObj_t*)fSeq)->fEntry = first;
        ((sFSeqObj_t*)fSeq)->ctx = 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 * fSeq, const fFSeqEntry_t * first )
{
   if( NULL != first )
   {
       ((sFSeqObj_t*)fSeq)->fEntry = first;
   }

   #if FSEQ_ENTRY_EXIT_SUPPORT
   #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
   ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
   #else
   ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
   #endif
   #endif
}

// @fSeqShutdown
// Shutdowns the running sequencer
// @fSeqObj - the sequencer object to shutdown 
void fSeqShutdown( xFSeqObj_t * fSeq )
{
   #if FSEQ_ENTRY_EXIT_SUPPORT
   #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
   ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
   #else
   ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
   #endif
   #endif
}

// @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 * fSeq )
{
    const fFSeqEntry_t * next;
    
    #if FSEQ_ENTRY_EXIT_SUPPORT
    const fFSeqEntry_t * next_deferred = NULL;
      #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
      while( (next = ((sFSeqObj_t*)fSeq)->fEntry->vtbl->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &next_deferred ), next ) )
      {   
          if( ((sFSeqObj_t*)fSeq)->fEntry != next ) // provide leave/enter events only if the operator has been changed
          {
          ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          ((sFSeqObj_t*)fSeq)->fEntry = next;
          ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          }
      }

      if( NULL != next_deferred  )
      {   // provide leave/enter events even if the operator has been changed (deffered feature)
          ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          ((sFSeqObj_t*)fSeq)->fEntry = next_deferred;
          ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
      }
      #else
      while( (next = ((sFSeqObj_t*)fSeq)->fEntry->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &next_deferred ), next ) )
      {        
          if( ((sFSeqObj_t*)fSeq)->fEntry != next ) // provide leave/enter events only if the operator has been changed
          {
          ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          ((sFSeqObj_t*)fSeq)->fEntry = next;
          ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          }
      }

      if( NULL != next_deferred  )
      {   // provide leave/enter events even if the operator has been changed (deffered feature)
          ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
          ((sFSeqObj_t*)fSeq)->fEntry = next_deferred;
          ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx );
      }
      #endif
    #else
      #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
      while( NULL != (next = ((sFSeqObj_t*)fSeq)->fEntry->vtbl->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &((sFSeqObj_t*)fSeq)->fEntry ), next) )
      {
          ((sFSeqObj_t*)fSeq)->fEntry = next;
      } 
      #else
      while( NULL != (next = ((sFSeqObj_t*)fSeq)->fEntry->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &((sFSeqObj_t*)fSeq)->fEntry ), next) )
      {
          ((sFSeqObj_t*)fSeq)->fEntry = next;
      }
      #endif
    #endif
}

#if 0
#if FSEQ_ALT_INHERIT_METHOD_SUPPORT == 0
// ----------------
  // Inheritance example:
     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
     }
#endif

#if FSEQ_ALT_INHERIT_METHOD_SUPPORT
// ------------------------------------------------------------------------------------------------
  // 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
     }
  // ------------------------------------------------------------------------------------------------
#endif
#endif