#include "core/config.h"
#if CONFIG_AUTOMAT_MODE || CONFIG_LEDS
#define ROUTINE_SEQUENCE_AUTOMAT_C
#include "rseq.h"
//------------------------------------------------------------------------------
#define NULL                                   ((void*)0)
#define NULL_Routine                           ((fRoutine_t*)NULL)
//------------------------------------------------------------------------------
bool rsa_sequence_init( sRoutineSequence_t * pSeq, void * arg, fRoutine_t ** papfRoutineList, uint32_t size );

bool rsa_sequence_insert_routine( sRoutineSequence_t * pSeq, fRoutine_t * routine );
bool rsa_sequence_remove_routine( sRoutineSequence_t * pSeq );
bool rsa_sequence_call( sRoutineSequence_t * pSeq );
bool rsa_sequence_reset( sRoutineSequence_t * pSeq );
bool rsa_sequence_clear( sRoutineSequence_t * pSeq );
#if RSA_USE_LOCKERS > 0
bool rsa_sequence_iinsert_routine( sRoutineSequence_t * pSeq, fRoutine_t * routine );
bool rsa_sequence_iremove_routine( sRoutineSequence_t * pSeq );
bool rsa_sequence_icall( sRoutineSequence_t * pSeq );
bool rsa_sequence_ireset( sRoutineSequence_t * pSeq );
bool rsa_sequence_iclear( sRoutineSequence_t * pSeq );
#endif
//------------------------------------------------------------------------------
static bool rsa_stub_locker( void * arg )
{
   return true;
}
//------------------------------------------------------------------------------
// rsa_sequence_init()
// Initialize sRoutineSequence_t before any operations
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be initialized
// * IN [ void * ] @arg - an user argument to be passed into each routine
// * IN [ fRoutine_t **] @papfRoutineList - the routine list to hold the values
// * IN [ uint32_t ] @size - the size of routine list array @papfRoutineList
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_init( sRoutineSequence_t * pSeq, void * arg, fRoutine_t ** papfRoutineList, uint32_t size )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq || NULL == papfRoutineList || 0 == size )
    return false;
#endif
  pSeq->count = 0;
  pSeq->idx = 0;
  pSeq->arg = arg;
  pSeq->papfRoutineList = papfRoutineList;
  pSeq->size = size;
  
#if RSA_USE_LOCKERS > 0
  pSeq->fLocker = rsa_stub_locker;
  pSeq->fUnlocker = rsa_stub_locker;
#endif
  
  return true;
}
//------------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0
// rsa_sequence_setlockers()
// Specifies a special Lock/Unlock function to be used during all procedures
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be initialized
// * IN [ fRoutine_t * ] @fLocker - a function to be used to lock the object @pSeq
// * IN [ fRoutine_t * ] @fUnlocker - a function to be used to unlock the object @pSeq
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_setlockers( sRoutineSequence_t * pSeq, fRoutine_t * fLocker, fRoutine_t * fUnlocker )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq || NULL == fLocker || NULL == fUnlocker )
    return false;
#endif
    
  pSeq->fLocker = fLocker;
  pSeq->fUnlocker = fUnlocker;  
  
  return true;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_iinsert_routine()
// Inserts a routine into the routine sequence without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// * IN [ fRoutine_t * ] @routine - a routine to be inserted
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0 
bool rsa_sequence_iinsert_routine( sRoutineSequence_t * pSeq, fRoutine_t * routine )
#else
bool rsa_sequence_insert_routine( sRoutineSequence_t * pSeq, fRoutine_t * routine )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq || NULL == routine )
    return false;
#endif
  if( pSeq->count < pSeq->size )
  {
      pSeq->papfRoutineList[ pSeq->count++ ] = routine;      
      
      return true;
  }
  
  return false;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0 
// rsa_sequence_insert_routine()
// Inserts a routine into the routine sequence with locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// * IN [ fRoutine_t * ] @routine - a routine to be inserted
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_insert_routine( sRoutineSequence_t * pSeq, fRoutine_t * routine )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq || NULL == routine )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_iinsert_routine( pSeq, routine );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_iskip_routine()
// Skips the current routine into the routine sequence without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0 
bool rsa_sequence_iskip_routine( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_skip_routine( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif

  if( pSeq->idx + 1 < pSeq->count )
  {
      pSeq->idx++;
      
      return true;
  }
  
  return false;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0 
// rsa_sequence_skip_routine()
// Skips the current routine into the routine sequence with locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_skip_routine( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_iskip_routine( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_iback_routine()
// Rolls the routine sequence back and make the previous routine to be called without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0 
bool rsa_sequence_iback_routine( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_back_routine( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif

  if( pSeq->idx - 1 > 0 )
  {
      pSeq->idx--;
      
      return true;
  }
  
  return false;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0 
// rsa_sequence_back_routine()
// Rolls the routine sequence back and make the previous routine to be called with locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_back_routine( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_iback_routine( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_iremove_routine()
// Removes the lastest routine from the routine sequence without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0
bool rsa_sequence_iremove_routine( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_remove_routine( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif
  if( pSeq->count > 0 )
  {
      pSeq->papfRoutineList[ --pSeq->count ] = NULL_Routine;

      return true;
  }
  
  return false;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0
// rsa_sequence_remove_routine()
// Removes the lastest routine from the routine sequence
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_remove_routine( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_iremove_routine( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_icall()
// Implements an ordinar call of the routines sequence  without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], end of sequence sign, true = sequence completed, false = sequence is in progress
#if RSA_USE_LOCKERS > 0
bool rsa_sequence_icall( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_call( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif
  if( (pSeq->count > 0) && (pSeq->idx < pSeq->count) )    
  {
      if( pSeq->papfRoutineList[ pSeq->idx ]( pSeq->arg ) )
      {
          pSeq->idx++;
      }
            
      return ( pSeq->idx >= pSeq->count );
  }
  
  return true;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0
// rsa_sequence_call()
// Implements an ordinar call of the routines sequence with locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], end of sequence sign, true = sequence completed, false = sequence is in progress
bool rsa_sequence_call( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_icall( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_ireset()
// Resets the routines sequence and makes it to be restarted without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0
bool rsa_sequence_ireset( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_reset( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif
  pSeq->idx = 0;
  
  return true;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0
// rsa_sequence_reset()
// Resets the routines sequence and makes it to be restarted with locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_reset( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_ireset( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
// -----------------------------------------------------------------------------
// rsa_sequence_iclear()
// Clears all the routines in the sequence without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
#if RSA_USE_LOCKERS > 0
bool rsa_sequence_iclear( sRoutineSequence_t * pSeq )
#else
bool rsa_sequence_clear( sRoutineSequence_t * pSeq )
#endif
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
#endif
  pSeq->count = 0;
  pSeq->idx = 0;
  
  return true;
}
// -----------------------------------------------------------------------------
#if RSA_USE_LOCKERS > 0
// rsa_sequence_reset()
// Clears all the routines in the sequence without locking
// Params: 
// * IN/OUT [ sRoutineSequence_t ] @pSeq - the sequence to be modified
// Returns:
// [ bool ], result of operation, true = success, false = error
bool rsa_sequence_clear( sRoutineSequence_t * pSeq )
{
#if RSA_STRICT_CHECKS > 0 
  if( NULL == pSeq )
    return false;
  
  if( NULL == pSeq->fLocker || NULL == pSeq->fUnlocker )
  {
      return false;
  }
#endif
  
  bool result = false;
  
  if( pSeq->fLocker( pSeq ) )
  {
      result = rsa_sequence_iclear( pSeq );
     
      pSeq->fUnlocker( pSeq );
  }
  
  return result;
}
#endif
#endif