#define SCPI_CORE_STATE__HANDLEERROR_GUARD
#include "app/scpi/CommandParserStates/handle_error.h"
#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#define SCPI_HANDLE_ERROR_C // access to some const variables in 'scpi_core_private.h'
#include "app/scpi/scpi_core_private.h"
#include "app/scpi/scpi_errq.h"

//----------------------------------------------------------------
// Refer to:
// [1] SCPI Specification, revision 1999.0
//     "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
// [2] Gpib Programming Tutorial, (http://g2pc1.bu.edu/~qzpeng/gpib/manual/GpibProgTut.pdf)
//     Electronics Group (http://www.few.vu.nl/~elec), 11 January 2000 Electronics Group
// [3] 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
//----------------------------------------------------------------
// Refer "21.8.1 The Error/Event Queue", [1]
// Refer "8.2.2 System sub-system", [2]

// =================================================================================
// @sHandleErrorContext_t
// State's private context typedef
typedef struct
{ 
   uint8_t lastEventCode;
}
sHandleErrorContext_t;

// =================================================================================
// @ctx_HandleError
// State's private context
sHandleErrorContext_t ctx_HandleError;

// =================================================================================
// @fsqvbl_HandleError
// State's virtual table

static void fsqe_HandleError( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static void fsql_HandleError( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static const struct fFSeqEntry_t * fsqf_HandleError( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pnext );

const fFSeqVTable_t fsqvbl_HandleError = 
{
     .f = fsqf_HandleError,
     .enter = fsqe_HandleError,  
     .leave = fsql_HandleError
};

// =================================================================================
// @fsqe_HandleError
// State's enter routine
static void fsqe_HandleError( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
{
    sScpiParserContext_t * global_ctx = ctx;
    sHandleErrorContext_t * private_ctx = fsq_GetPrivateCtxRef(this);

    (void)global_ctx, (void)private_ctx;

    private_ctx->lastEventCode = global_ctx->sEvent.eCode;

    if( SCPI_ERROR_STATE != global_ctx->sExecution.runtimeError )
    {
        if( ! errq_push( &global_ctx->sExecution.xScpiErrorQueue,
                         global_ctx->sExecution.runtimeError,
                         global_ctx->sExecution.xScpiErrorMessage ) )
        {
            // Error Queue Overflow
            global_ctx->sExecution.errorQueueOverflow = true;

            // "21.8.1 The Error/Event Queue", [1]
            // "21.8.11 Device-Specific Error", [1]
            // "11.5.1.1.6 Bit 3 N Device-Specific ERROR (DDE)", [3]
            GPIBMachine.fGPIB_set_event_status_register_device_specific_error_state(
                                                  &global_ctx->sGPIB.registers, true );

            global_ctx->sExecution.runtimeError = SCPI_ERROR_SUCCESS; // clear error code
            global_ctx->sEvent.eStatus = eScpiStatus_success; // clear error status
        }
        else
        {
            // Notify GPIB: error available in the queue
            //   "4 Instrument status", [2]
            //   "4.1 Status Byte registers (STB and SRE)", [2]
            //   "11.2.2.3 Master Summary Status", [4]
            //   "11.3.2.4 Clearing the Service Request Enable Register", [3] 
            GPIBMachine.fGPIB_set_error_available_bit( &global_ctx->sGPIB.registers, true );

            switch( SCPI_ERROR_CLASS(global_ctx->sExecution.runtimeError) )
            {
               // ------------ Reasons: --------------
               // "6.1.6.1.1 Parser Errors", [3]
               // "6.5.4 Command Error", [3]
               case SCPI_ERROR_CLASS_COMMAND:
                    GPIBMachine.fGPIB_set_event_status_register_command_error_state(
                                                  &global_ctx->sGPIB.registers, true );
               break;

               // ------------ Reasons: --------------
               // "6.5.5 Execution Error", [3]
               // "11.5.1.1.5 Bit 4 N Execution ERROR (E)", [3]
               // "<...> the device shall continue parsing the input stream. <...>", [3]
               case SCPI_ERROR_CLASS_EXECUTION:
                    GPIBMachine.fGPIB_set_event_status_register_execution_error_state(
                                                  &global_ctx->sGPIB.registers, true );

                    // "11.5.1.1.5 Bit 4 N Execution ERROR (E)", [3]
                    // "<...> the device shall continue parsing the input stream. <...>", [3]
                    global_ctx->sEvent.eCode = eScpiEventContinue; // change event code to continue parsing
               break;

               // "11.5.1.1.6 Bit 3 N Device-Specific ERROR (DDE)", [3]
               case SCPI_ERROR_CLASS_DEVICE:
                    GPIBMachine.fGPIB_set_event_status_register_device_specific_error_state(
                                                  &global_ctx->sGPIB.registers, true );
               break;

               // ------------ Reasons: --------------
               // "6.3.1.3 QUERY State", [3]
               // "6.3.1.7 DEADLOCK State", [3]
               // "6.3.2.2 UNTERMINATED Action", [3]
               // "6.3.2.3 INTERRUPTED Action", [3]
               // "6.5.7 Query Error", [3]
               case SCPI_ERROR_CLASS_QUERY:
                    GPIBMachine.fGPIB_set_event_status_register_query_error_state(
                                                  &global_ctx->sGPIB.registers, true );
               break;
            }

            global_ctx->sExecution.runtimeError = SCPI_ERROR_SUCCESS; // clear error code
            global_ctx->sEvent.eStatus = eScpiStatus_success; // clear error status
        }
    }
}

// =================================================================================
// @fsql_HandleError
// State's leave routine
static void fsql_HandleError( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
{
    sScpiParserContext_t * global_ctx = ctx;
    sHandleErrorContext_t * private_ctx = fsq_GetPrivateCtxRef(this); 

    (void)ctx, (void)private_ctx, (void)global_ctx;
}

// =================================================================================
// @fsqf_HandleError
// State's body routine
static const struct fFSeqEntry_t * fsqf_HandleError( const struct fFSeqEntry_t * this, 
                                                                 tFSeqCtx_t ctx, 
                                                                  const struct fFSeqEntry_t * * pDeferredNext )
{
    // --------------------------------------------------------------------------
    const struct fFSeqEntry_t * nextstate = NULL; // do not change state, do not call next state
    sScpiParserContext_t * global_ctx = ctx;
    sHandleErrorContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
    uint8_t nextEvent = eScpiEventEmpty;
    // --------------------------------------------------------------------------
    switch( global_ctx->sEvent.eCode )
    {
       case eScpiEventRestart:
       {
           // go to reset state
           nextstate = fsq_GetStateById(this,eParserResetState);
       }
       break;

       case eScpiEventContinue:
       {
           // Search for the following message unit separator
           // goto next state: eProcessEndOfProgramData
           nextstate = fsq_GetStateById(this,eProcessEndOfProgramData); 
           nextEvent = private_ctx->lastEventCode;
           (void)global_ctx->sEvent.eStatus;; // already cleared
       }
       break;

       case eScpiEventError:  // transport error
       {
           global_ctx->sEvent.eStatus = eScpiStatus_success;

           (void)nextstate; // keep this state
       }
       break;

       case eScpiEventRead:   // read event during Error condition:
       case eScpiEventWrite:  // command parse error -> error state
       {
           // Respond with empty string:
           if( global_ctx->sRead.nDataLength == 0 ) // no response in output buffer
           if( global_ctx->sRead.nBufferSize >= 1 ) // there is vacant room in output buffer
           {
              scpi_WriteCharOutput( ' ' );

              // "Query error", "11.5.1.1.7 Bit 2", [3]
              // "An attempt is being made to read data from the Output Queue when no output is either present or pending"
              GPIBMachine.fGPIB_set_event_status_register_query_error_state( &global_ctx->sGPIB.registers, true );
           }

           global_ctx->sEvent.eStatus = eScpiStatus_success;
           global_ctx->sMessage.r_bEndOfMessage = true;

           scpi_UpdateMessageAvailable();

           (void)nextstate; // keep this state
       }
       break;
    }
    
    global_ctx->sEvent.eCode = nextEvent;
    return nextstate;
}
