#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#define SCPI_CORE_C       // access to const values in 'scpi_core_private.h' structures
#include "app/scpi/scpi_core.h"
#include "app/scpi/scpi_core_private.h"
#include "app/scpi/scpi_parser.h"
#include "app/scpi/scpi_commands.h"
#include "app/scpi/scpi_errq.h"
#include "app/scpi/CommandParserStates/reset_state.h"
#include "app/scpi/CommandParserStates/search_for_command.h"
#include "app/scpi/CommandParserStates/process_command.h"
#include "app/scpi/CommandParserStates/process_data.h"
#include "app/scpi/CommandParserStates/endof_process_data.h"
#include "app/scpi/CommandParserStates/handle_error.h"
#include <stdio.h>
#include "my_assert.h"


//----------------------------------------------------------------
// Refer to:
// [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992)
//     "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE
//      Standard Digital Interface for Programmable Instrumentation"
// [2] "Programming Guide", "Keysight Models 664xA, 665xA, 667xA, 668xA, and 669xA GPIB DC Power Supplies", Edition 2, December 2014, "5964-8269"
static eScpiStatus_t fSCPIInit( );
static eScpiStatus_t fSCPIReset( );
static eScpiStatus_t fSCPINewOutTransfer( );
static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer );
static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM );
static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB );
static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx );
static eScpiStatus_t fSCPIDeInit();

const sSCPILibHandle_t sSCPILibHandle = {

   .fInit = fSCPIInit,
   .fReset = fSCPIReset,
   .fNewTransfer = fSCPINewOutTransfer,
   .fWrite = fSCPIWrite,
   .fRead = fSCPIRead,
   .fNotificationRead = fSCPINotificationRead,
   .fError = fSCPIError,
   .fDeInit = fSCPIDeInit
};

// -----------------------------------------------------------
// @seqScpiProcessor
// The SCPI module functor-sequencer for command iterator
static xFSeqObj_t seqScpiProcessor;
// -----------------------------------------------------------
// @ctx_ScpiParser
// SCPI parser common context
static sScpiParserContext_t ctx_ScpiParser;
// -----------------------------------------------------------

const sScpiParserStateEntry_t sParserStateMap[] = {

   // State: eParserResetState
   [eParserResetState] = SCPI_STATE_PARSERRESETSTATE,
                          
   // State: SearchForCommandHeader
   [eSearchForCommandHeader] = SCPI_STATE_SEARCH4COMMAND,
 
   // State: ProcessCommandHeader
   [eProcessCommandHeader] = SCPI_STATE_PROCESSCOMMAND,

   // State: ProcessProgramData
   [eProcessProgramData] = SCPI_STATE_PROCESSDATA,

   // State: EndOfProcessProgramData
   [eProcessEndOfProgramData] = SCPI_STATE_ENDOFPROCESSDATA,

   // State: HandleError
   [eHandleError] = SCPI_STATE_HANDLEERROR,
};

// -----------------------------------------------------------
// @fsq_GetPrivateCtxRef
// Get access to the state's private context by pointer
// @this - the state entry, passed into main state function, or 
// ... entry-/leave- routines.
// Returns: the state private context pointer that represented by the
// @ctx_ref field in @sScpiParserStateEntry_t structure.
void * fsq_GetPrivateCtxRef( const struct fFSeqEntry_t * this )
{
     // Refer to: @sParserStateMap[], @sScpiParserStateEntry_t
     const sScpiParserStateEntry_t * container = (const sScpiParserStateEntry_t *)this;

     return container->ctx_ref;
}

// -----------------------------------------------------------
// @fsq_GetStateById_unsafe
// Get the state entry by state identifier @sid
// @sid - the state identifier (eScpiParserStateId_t)
// Returns: the state entry pointer
static const struct fFSeqEntry_t * fsq_GetStateById_unsafe( eScpiParserStateId_t sid )
{
     if( sid < cellsof(sParserStateMap) )
       return &sParserStateMap[sid].entry;

     return NULL; 
}

// -----------------------------------------------------------
// @fsq_GetStateById
// Get the state entry by state identifier @sid
// @this - current state pointer
// @sid - the state identifier (eScpiParserStateId_t)
// Returns: the state entry pointer
// Note: the caller must be the state from the same entity
const struct fFSeqEntry_t * fsq_GetStateById( const struct fFSeqEntry_t * this, eScpiParserStateId_t sid )
{
     my_assert( sid < cellsof(sParserStateMap) );

     if( sid < cellsof(sParserStateMap) )
     {
       ptrdiff_t range_begin = (ptrdiff_t)&sParserStateMap[0];
       ptrdiff_t range_end = (ptrdiff_t)&sParserStateMap[ cellsof(sParserStateMap) - 1 ];

       my_assert( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end );

       if( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end )
       return &sParserStateMap[sid].entry;
     }
     return NULL; 
}

// -----------------------------------------------------------
// @fsq_RaiseErrorEx
// Generate internal error event to place it into SCPI Error Log
// @scpiError - SCPI error code
// @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number
// @msgDescription - optional string message with @msgDescLength length
// @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription)
// @msgDescriptionEx - extended optional string message with @msgDescLength length
// @msgDescriptionEndEx - end of extended optional message string, is used to determine the string length (@msgDescriptionEx)
void fsq_RaiseErrorEx( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd,
                                                              const char * msgDescriptionEx, const char * msgDescriptionEndEx )
{
   ctx_ScpiParser.sExecution.runtimeError = scpiError;

   size_t nPos = 0;
   size_t nSize = SCPI_ERRORMSG_MAX - 1;

   if( NULL != msgHeader )
   {
       char errNumber[8] = { '\0' };
       _snprintf( errNumber, sizeof(errNumber), "%d, ", scpiError );

       size_t plen = strlen( errNumber );

       if( plen < nSize )
       {
           strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], errNumber );
           nSize -= plen;
           nPos += plen;
       }

       plen = strlen( msgHeader );

       my_assert( plen < SCPI_ERRORMSG_MAX );

       if( plen < nSize )
       {
           strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgHeader );
           nSize -= plen;
           nPos += plen;
       }
   }

   if( NULL == msgDescription || NULL == msgDescriptionEnd )
   {
       msgDescription = msgDescriptionEx;
       msgDescriptionEnd = msgDescriptionEndEx;
       msgDescriptionEx = NULL;
       msgDescriptionEndEx = NULL;
   }

   if( NULL != msgDescription && NULL != msgDescriptionEnd )
   {
       // remove trailing NL-character
       if( scpi_isnl( *(msgDescriptionEnd-1) ) )
       {
           msgDescriptionEnd--;
       }

       if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription == 0 )
       {
           msgDescription = msgDescriptionEx;
           msgDescriptionEnd = msgDescriptionEndEx;
           msgDescriptionEx = NULL;
           msgDescriptionEndEx = NULL;
       }

       if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription > 0 )
       {
           size_t msgDescLength = (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription;
           size_t plen = msgDescLength;

           if( NULL != msgHeader )
           {
               // Default message mode: @msgHeader and @msgDescription

               const char prfstr[] = ": '";  plen += strlen(prfstr);
               const char psfstr[] = "'";    plen += strlen(psfstr);

               if( plen < nSize )
               {
                   // Full message fits to the buffer

                   strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
                   strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength );
                   strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
               }
               else
               {
                   const char sfxstr[] = "..."; plen = strlen(sfxstr);
                   plen += strlen(prfstr);
                   plen += strlen(psfstr);

                   // there is free space for a part of description?
                   if( nSize >= plen )
                   {
                       msgDescLength = nSize - plen;

                       if( msgDescLength > 8 )
                       {
                           strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
                           strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength );
                           strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr );
                           strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );

                           plen += msgDescLength; 
                       }
                       else
                         plen = 0;
                   }
                   else
                     plen = 0;
               }
           }
           else
           {
               // Custom message mode: Only @msgDescription
               if( plen < nSize )
               {
                   strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescription, msgDescLength );
               }
               else
               {
                   plen = 0;
               }
           }

           nSize -= plen;
           nPos += plen;
       }
   }

   if( NULL != msgDescriptionEx && NULL != msgDescriptionEndEx )
   {
       // remove trailing NL-character
       if( scpi_isnl( *(msgDescriptionEndEx-1) ) )
       {
           msgDescriptionEndEx--;
       }

       if( (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx > 0 )
       {
           size_t msgDescLength = (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx;
           size_t plen = msgDescLength;

           if( NULL != msgHeader || NULL != msgDescription )
           {
               // Default message mode: @msgHeader or @msgDescription already exist

               const char prfstr[] = " in '";  plen += strlen(prfstr);
               const char psfstr[] = "'";    plen += strlen(psfstr);

               if( plen < nSize )
               {
                   // Full message fits to the buffer

                   strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
                   strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength );
                   strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
               }
               else
               {
                   const char sfxstr[] = "..."; plen = strlen(sfxstr);
                   plen += strlen(prfstr);
                   plen += strlen(psfstr);

                   // there is free space for a part of description?
                   if( nSize >= plen )
                   {
                       msgDescLength = nSize - plen;

                       if( msgDescLength > 12 )
                       {
                           strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
                           strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength );
                           strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr );
                           strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );

                           plen += msgDescLength; 
                       }
                       else
                         plen = 0;
                   }
                   else
                     plen = 0;
               }
           }
           else
           {
               // Custom message mode: Only @msgDescriptionEx
               if( plen < nSize )
               {
                   strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescriptionEx, msgDescLength );
               }
               else
               {
                   plen = 0;
               }
           }

           nSize -= plen;
           nPos += plen;
       }
   }

   ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos] = '\0';
}

// -----------------------------------------------------------
// @fsq_RaiseError
// Generate internal error event to place it into SCPI Error Log
// @scpiError - SCPI error code
// @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number
// @msgDescription - optional string message with @msgDescLength length
// @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription)
void fsq_RaiseError( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd )
{
   fsq_RaiseErrorEx( scpiError, msgHeader, msgDescription, msgDescriptionEnd, NULL, NULL );
}

// -----------------------------------------------------------
int32_t scpi_WriteChunkOutput( const void * src, size_t length )
{
    // This routine may be called even if no output buffer is prepared.
    // Check it
    if( NULL == ctx_ScpiParser.sRead.pBufferIdx
     ||    0 == ctx_ScpiParser.sRead.nBufferSize
     || NULL == ctx_ScpiParser.sRead.pBuffer )
    {
       return 0;
    }

    size_t chunkSize = ((ctx_ScpiParser.sRead.nBufferSize > length)? length : ctx_ScpiParser.sRead.nBufferSize); // min(nBufferSize, length)

    if( chunkSize > 0 )
    {
        memcpy( ctx_ScpiParser.sRead.pBufferIdx, src, chunkSize );
        ctx_ScpiParser.sRead.pBufferIdx += chunkSize;
        ctx_ScpiParser.sRead.nDataLength += chunkSize;
        ctx_ScpiParser.sRead.nTotalLength += chunkSize;
        ctx_ScpiParser.sRead.nBufferSize -= chunkSize;
    }

    return chunkSize;
}

int32_t scpi_WriteCharOutput( uint8_t ch )
{
    return scpi_WriteChunkOutput( &ch, sizeof(ch) );
}

// -----------------------------------------------------------
// @scpi_UpdateMessageAvailable
// Updates the message available status, MAV
// "6.1.10.2.1 Message Available Message (MAV)", [1]
void scpi_UpdateMessageAvailable()
{
    // Check if .sRead structure is available:
    // Note this routine can be called on Read and Write events not only from SCPI-core, but
    // by changing Write event to Read event, in this case .sRead structure is initialized.
    if( (NULL != ctx_ScpiParser.sRead.pBuffer) && (0 != ctx_ScpiParser.sRead.nBufferSize) )
    {
        // Message available: only if EOM is reset the MAV is set if is there data in the output buffer.
        // Otherwise, if EOM is set, these bytes will be send as soon as possible, so this means there no data to send anymore
        // in SCPI core.
        bool message_available = (ctx_ScpiParser.sRead.nDataLength > 0) && (!ctx_ScpiParser.sMessage.r_bEndOfMessage);

        // Is there data to send to host: set Message-Available indicator
        GPIBMachine.fGPIB_set_message_available_bit( &ctx_ScpiParser.sGPIB.registers, message_available );
    }
}

// -----------------------------------------------------------
// @GPIB_Init
// Initialize the GPIB machine state to the startup defaults
static void GPIB_Init()
{
    GPIBMachine.fGPIB_set_event_status_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 );       // Reset ESE

    GPIBMachine.fGPIB_set_service_request_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 );    // Reset SRE
    GPIBMachine.fGPIB_set_event_summary_enable_state( &ctx_ScpiParser.sGPIB.registers, true );      // Set SRE.ESB
    GPIBMachine.fGPIB_set_message_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true );  // Set SRE.MAV
    GPIBMachine.fGPIB_set_error_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true );    // Set SRE.EAV

    // "11.5.1.1.2 Bit 7 - Power On (PON)", [1]
    // "6.1.8.2.1 Power-On Message (pon)", [1]
    // "6.3.1.1 IDLE State", [1]
    // "The PON (Power-On) Bit", "Service Request Enable Register", [2]
    GPIBMachine.fGPIB_set_event_status_register_power_on_state( &ctx_ScpiParser.sGPIB.registers, true );              // Set ESR.PON

    // "5.6.1 Control and Operation Definitions", [1]
    GPIBMachine.fGPIB_set_event_status_register_user_request_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.5.1.1.4 Bit 5 - Command ERROR (CME)", [1]
    GPIBMachine.fGPIB_set_event_status_register_command_error_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.5.1.1.5 Bit 4 - Execution ERROR (E)", [1]
    GPIBMachine.fGPIB_set_event_status_register_execution_error_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.5.1.1.6 Bit 3 - Device-Specific ERROR (DDE)", [1]
    GPIBMachine.fGPIB_set_event_status_register_device_specific_error_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.5.1.1.7 Bit 2 - Query ERROR (QYE)", [1]
    GPIBMachine.fGPIB_set_event_status_register_query_error_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.5.1.1.8 Bit 1 - Request Control (RQC)", [1]
    GPIBMachine.fGPIB_set_event_status_register_request_control_state( &ctx_ScpiParser.sGPIB.registers, false );

    // Set OPC:
    // Note, since the device supports only syncrounious commands, the OPC can be set once and have never been reset.
    // "11.5.1.1.9 Bit 0 - Operation Complete (OPC)", [1]
    // "<...> This event bit is generated in response to the *OPC command. <...>"
    GPIBMachine.fGPIB_set_event_status_register_operation_complete_state( &ctx_ScpiParser.sGPIB.registers, false );

    // "11.3.3 Service Request Generation", [1]
    // Service Request Generation - reset status
    GPIBMachine.fGPIB_set_service_request_status( &ctx_ScpiParser.sGPIB.registers, false );
}

// -----------------------------------------------------------
// @fSCPIInit
// Initialize parser
static eScpiStatus_t fSCPIInit()
{
    // prepare global context: see @eParserResetState state dispatch function
    (void)ctx_ScpiParser;
    
    // construct state machine object, link the first state 'eSearchForCommandHeader'
    fSeqConstruct( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState), &ctx_ScpiParser );

    // Initialize SCPI Error Log Queue
    ctx_ScpiParser.sExecution.errorQueueOverflow = false; // reset overflow indicator
    bool errqrc = errq_init( &ctx_ScpiParser.sExecution.xScpiErrorQueue,
                              ctx_ScpiParser.sExecution.xScpiErrorLogStorage, 
                       sizeof(ctx_ScpiParser.sExecution.xScpiErrorLogStorage) );

    my_assert( errqrc ); (void)errqrc; 

    // "5.8 Device Clear Requirements", [1]
    // In Device Clear event: DO NOT clear Error Queue and any bits other than clearing MAV bit in GPIB
    // Due to the 'reset_state' is executes on DEVICE CLEAR (INITIATE_CLEAR), it is impossible to
    // initialize GPIB in 'reset_state'.
    // So, the initialization is placed here:
    GPIB_Init();

    // startup the SCPI parser state machine
    fSeqStartup( &seqScpiProcessor, NULL );

    // ... It is requried to execute the ResetState job: initialize and
    // ... go next state
    fSeqDispatch( &seqScpiProcessor );

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPIReset
// Reset parser
static eScpiStatus_t fSCPIReset()
{
    // shutdown the SCPI parser state machine
    fSeqShutdown( &seqScpiProcessor );

    // startup the SCPI parser state machine a reset state
    fSeqStartup( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState) );

    // process the state machine state:
    // ... It is requried to execute the ResetState job: initialize and
    // ... go next state
    fSeqDispatch( &seqScpiProcessor ); 

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPINewOutTransfer
// New Transfer Event
static eScpiStatus_t fSCPINewOutTransfer()
{    
    // prepare global context: set the buffer properties
    ctx_ScpiParser.sWrite.pData = NULL;
    ctx_ScpiParser.sWrite.nDataLength = NULL;
    ctx_ScpiParser.sMessage.w_bEndOfMessage = false;
    ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // IN-transfer is reset
    ctx_ScpiParser.sMessage.bQuery = false;
    ctx_ScpiParser.sMessage.pStr = NULL;
    ctx_ScpiParser.sMessage.bLeadResponseSep = false;
    ctx_ScpiParser.sRead.pBuffer = NULL;
    ctx_ScpiParser.sRead.pBufferIdx = NULL;
    ctx_ScpiParser.sRead.nDataLength = 0;
    ctx_ScpiParser.sRead.nBufferSize = 0;
    ctx_ScpiParser.sRead.nTotalLength = 0;
    ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
  //ctx_ScpiParser.sEvent.bNewTransfer = true; // pass New Transfer indicator
    ctx_ScpiParser.sEvent.eCode = eScpiEventRestart; // send RESTART event
    
    // process the state machine state
    fSeqDispatch( &seqScpiProcessor );

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPIWrite
// Write parser event
static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer )
{
    my_assert( pData );
    my_assert( nDataLen );    

    // Bufferizing input command string:
    if( bNewTransfer )
    {
        // New transfer: cleanup cached input string and replace it with new one

        my_assert( nDataLen < sizeof(ctx_ScpiParser.sCache.inputCommand) );

        if( nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) )
        {
            return eScpiStatus_failed; // buffer overflow
        }

        memcpy( &ctx_ScpiParser.sCache.inputCommand[0], pData, nDataLen );
        ctx_ScpiParser.sCache.nBytes = nDataLen; 
    }
    else
    {
        // Continue transfer: concatinate cached input string with new part

        if( ctx_ScpiParser.sCache.nBytes + nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) )
        {
            return eScpiStatus_failed; // buffer overflow
        }

        memcpy( &ctx_ScpiParser.sCache.inputCommand[ctx_ScpiParser.sCache.nBytes], pData, nDataLen );
        ctx_ScpiParser.sCache.nBytes += nDataLen; 
    }

    // prepare global context: set the buffer properties
    ctx_ScpiParser.sWrite.pData = ctx_ScpiParser.sCache.inputCommand;
    ctx_ScpiParser.sWrite.nDataLength = ctx_ScpiParser.sCache.nBytes;
    ctx_ScpiParser.sMessage.w_bEndOfMessage = bEndOfMessage;
    (void)ctx_ScpiParser.sMessage.r_bEndOfMessage;
    ctx_ScpiParser.sMessage.bQuery = false;
    ctx_ScpiParser.sMessage.pStr = pData;
    ctx_ScpiParser.sMessage.pEnd = (pData + nDataLen);
    ctx_ScpiParser.sMessage.bLeadResponseSep = false;
    ctx_ScpiParser.sRead.pBuffer = NULL;
    ctx_ScpiParser.sRead.pBufferIdx = NULL;
    ctx_ScpiParser.sRead.nBufferSize = 0;
    ctx_ScpiParser.sRead.nDataLength = 0;
    ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
    (void)ctx_ScpiParser.sRead.nTotalLength;
  //ctx_ScpiParser.sEvent.bNewTransfer = bNewTransfer; // pass New Transfer indicator
    ctx_ScpiParser.sEvent.eCode = eScpiEventWrite; // send WRITE event
    
    // process the state machine state
    fSeqDispatch( &seqScpiProcessor );

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPIRead
// Read parser event
// @pBuffer - response buffer pointer
// @nBufferLen - response buffer size
// @pnBytesOut - pointer to the variable to store the value of written bytes to response buffer
// @pEOM - pointer to the variable to store the End-Of-Message indicator. This indicator is set by
// the SCPI library to notify the caller to specify SCPI End-of-message flag in Bulk-IN header that
// is responsible for the stream control. If this flag is set, the Host must generate next READ event
// even if the current buffer is not completely full. It is used together with complex command that
// require several responses. Note: this value is only valid if the return value is not the error code.
static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM )
{
    my_assert( pBuffer );
    my_assert( nBufferLen );
    my_assert( pnBytesOut );

    // prepare global context: set the buffer properties
    (void)ctx_ScpiParser.sWrite.pData; // keep value
    (void)ctx_ScpiParser.sWrite.nDataLength; // keep value
    (void)ctx_ScpiParser.sMessage.w_bEndOfMessage; // keep value
    ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // reset by default, need to set to interrupt reading
    (void)ctx_ScpiParser.sMessage.bQuery; // keep value
    (void)ctx_ScpiParser.sMessage.pStr; // keep value
    (void)ctx_ScpiParser.sMessage.pEnd; // keep value
    (void)ctx_ScpiParser.sMessage.bLeadResponseSep; // keep value
    ctx_ScpiParser.sRead.pBuffer = pBuffer;
    ctx_ScpiParser.sRead.pBufferIdx = pBuffer;
    ctx_ScpiParser.sRead.nBufferSize = nBufferLen;
    ctx_ScpiParser.sRead.nDataLength = 0;
    ctx_ScpiParser.sRead.vLastBufferIdx = pBuffer;
    (void)ctx_ScpiParser.sRead.nTotalLength;
    ctx_ScpiParser.sEvent.eCode = eScpiEventRead; // send READ event
    ctx_ScpiParser.sEvent.eStatus = eScpiStatus_failed; // forward set by default
  //ctx_ScpiParser.sEvent.bNewTransfer = false;
    // process the state machine state
    fSeqDispatch( &seqScpiProcessor );

    *pnBytesOut = ctx_ScpiParser.sRead.nDataLength; // @nDataLength shall be modified
    *pEOM = ctx_ScpiParser.sMessage.r_bEndOfMessage;
#warning SCPI missing EOM bit set if TransferSize is full  (return eScpiStatus_need_data)
    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPINotificationRead
// Read notification data (Interrupt-IN read)
static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB )
{
    my_assert( pBuffer );
    my_assert( nBufferLen );
    my_assert( pnBytesOut );

    // "11.2.2.1 Reading with a Serial Poll", [1]
    // "11.2.2.2 Reading With the *STB? Query", [1]
    GPIBMachine.fGPIB_get_status_byte_serial_poll( &ctx_ScpiParser.sGPIB.registers, pSTB );

    *pnBytesOut = 0; // no more bytes will be returned in notification, [2] 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request
    ctx_ScpiParser.sEvent.eStatus = eScpiStatus_success; // forward set by default
    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}
// -----------------------------------------------------------
// @fSCPIError       
// Error notification from USBTMC level.
// @error - error id, see @SCPILIB_ERROR_* macro group
// @errorCtx - error dependent context, optional;
// Return:
// - eScpiStatus_success: error processed successfully
// - eScpiStatus_failed: unrecoveral error, need to process it on USBTMC level.
static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx )
{
    // shall not be SCPILIB_ERROR_SUCCESS  
    my_assert( error != SCPILIB_ERROR_SUCCESS );

    // prepare global context: set the buffer properties
    ctx_ScpiParser.sWrite.pData = NULL;
    ctx_ScpiParser.sWrite.nDataLength = 0;
    (void)ctx_ScpiParser.sMessage.w_bEndOfMessage;
    (void)ctx_ScpiParser.sMessage.r_bEndOfMessage;
    (void)ctx_ScpiParser.sMessage.bQuery;
    ctx_ScpiParser.sMessage.pStr = NULL;
    ctx_ScpiParser.sMessage.pEnd = NULL;
    (void)ctx_ScpiParser.sMessage.bLeadResponseSep;
    ctx_ScpiParser.sRead.pBuffer = NULL;
    ctx_ScpiParser.sRead.pBufferIdx = NULL;
    ctx_ScpiParser.sRead.nBufferSize = 0;
    ctx_ScpiParser.sRead.nDataLength = 0;
    ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
    (void)ctx_ScpiParser.sRead.nTotalLength;
    ctx_ScpiParser.sEvent.eCode = eScpiEventError; // send ERROR event
    ctx_ScpiParser.sEvent.eStatus = eScpiStatus_invalid;

    switch( error )
    {
       case SCPILIB_ERROR_BUFFER_OVERFLOW:
       {
            fsq_RaiseError( SCPI_ERROR_INBUF_OVERFLOW, SCPI_ERROR_INBUF_OVERFLOW_MSG, NULL, 0 );
       }
       break;

       default: return eScpiStatus_invalid; // invalid error code, interrupt processing
    }

    // process the state machine state
    fSeqDispatch( &seqScpiProcessor );

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}

// -----------------------------------------------------------
// @fSCPIDeInit
// Deinitialize parser
static eScpiStatus_t fSCPIDeInit()
{
    // prepare global context:
    ctx_ScpiParser.sEvent.eCode = eScpiEventEmpty; // no event occurred

    // shutdown the SCPI parser state machine
    fSeqShutdown( &seqScpiProcessor );

    return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
}
