#define SCPI_CORE_STATE__PROCESSCOMMAND_GUARD
#include "app/scpi/CommandParserStates/process_command.h"
#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#define SCPI_PROCESS_COMMAND_C // access to some const variables in 'scpi_core_private.h'
#include "app/scpi/scpi_core_private.h"
#include "app/scpi/scpi_commands.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] SCPI Specification, revision 1999.0
//     "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
// [3] 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
//----------------------------------------------------------------

// =================================================================================
// @sProcessCommandHeaderContext_t
// State's private context typedef
typedef struct
{
   uint8_t      eCmdHeader_type;
   bool         bKeepParserContext;
}
sProcessCommandHeaderContext_t;

// =================================================================================
// @ctx_ProcessCommandHeader
// State's private context
sProcessCommandHeaderContext_t ctx_ProcessCommandHeader;

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

static void fsqe_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static void fsql_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static const struct fFSeqEntry_t * fsqf_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pDeferredNext );

const fFSeqVTable_t fsqvbl_ProcessCommandHeader = 
{
     .f = fsqf_ProcessCommandHeader,
     .enter = fsqe_ProcessCommandHeader,  
     .leave = fsql_ProcessCommandHeader
};

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

    assert( private_ctx );
    assert( global_ctx );

    const char * pCmdHeader_shead = NULL;
    const char * pCmdHeader_stail = NULL;

    private_ctx->bKeepParserContext = false; // reset parser context at next call

    // @eCmdHeader_type: need to identify query/command (@eScpiEntityType_t)
    private_ctx->eCmdHeader_type = eScpiEntityTypeInvalid; 
    
    // Retrieve the command header entity info:
    getParsedEntityDetails( &(global_ctx->sParser.xCtxObj),
                  (void const**)&pCmdHeader_shead,
                  (void const**)&pCmdHeader_stail,
                                &private_ctx->eCmdHeader_type );

    assert( pCmdHeader_shead );
    assert( pCmdHeader_stail );
    assert( eScpiEntityTypeInvalid != private_ctx->eCmdHeader_type );
    
    // update current input buffer iterator
    global_ctx->sMessage.pStr = (const uint8_t*)pCmdHeader_stail;

    // search for command handler
    // [3], "3.4 Omitting the header path",
    // This first header is assumed to be found in the root branch. So, it is required to save
    // the branch if the command is found.
    global_ctx->sParser.xHandler = scpiSearchCommandHandler(
                                               (const sTreeList_t **)&global_ctx->sParser.xCmdBranch,
                                               pCmdHeader_shead,
                                               pCmdHeader_stail, 
                                               private_ctx->eCmdHeader_type,
                                               &global_ctx->sParser.xHandlerToken,
                                               global_ctx->sParser.bFirstCommandIndicator );

    // reset the first command indicator
    global_ctx->sParser.bFirstCommandIndicator = false;

    if( NULL == global_ctx->sParser.xHandler )
    {
        global_ctx->sParser.xHandlerToken.shead = pCmdHeader_shead;
        global_ctx->sParser.xHandlerToken.stail = pCmdHeader_stail;

        // Enter error state
        fsq_RaiseError( SCPI_ERROR_UNDEFHEADER_ERROR, SCPI_ERROR_UNDEFHEADER_ERROR_MSG, pCmdHeader_shead, pCmdHeader_stail );
    }
}

// =================================================================================
// @fsql_ProcessCommandHeader
// State's leave routine
static void fsql_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
{
}

// =================================================================================
// @fsqf_ProcessCommandHeader
// State's body routine
static const struct fFSeqEntry_t * fsqf_ProcessCommandHeader( const struct fFSeqEntry_t * this, 
                                                                tFSeqCtx_t ctx, 
                                                                  const struct fFSeqEntry_t * * pnext )
{
    sScpiParserContext_t * global_ctx = ctx;
    sProcessCommandHeaderContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
    const struct fFSeqEntry_t * nextstate = fsq_GetStateById(this,eHandleError); // go next state: eHandleError by default 

    assert( private_ctx );
    assert( global_ctx ); 

    // Formal checking
    if( eScpiEventRestart == global_ctx->sEvent.eCode )
    {
        // go to reset state
        nextstate = fsq_GetStateById(this,eParserResetState);
        goto L_fsqf_ProcessCommandHeader_EXIT;
    }

    if( eScpiEventError == global_ctx->sEvent.eCode ) // Transport error.
    {
        // Enter error state
        nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
        goto L_fsqf_ProcessCommandHeader_EXIT;
    }

    // check if has the handler been found:
    if( NULL == global_ctx->sParser.xHandler )
    {
        global_ctx->sEvent.eStatus = eScpiStatus_failed;

        // Note: impossible to complete parsing the rest part of the message 
        // due to the data stage of the undefinded command can not be recognized.

        return nextstate; // Note: go to next state: eHandleError (default)
    }

    // It is required to skip Command Program Header separator ("7.4.3, [1]"):
    {
      // Retrieve the command header entity info to get the tail pointer if the last parsed entity:
      const char * shead = (const char *)global_ctx->sMessage.pStr;
          
      // determine the new tail pointer (@sWrite.nDataLength may change each call)
      const char * stail = (const char *)global_ctx->sMessage.pEnd;
      
      assert( shead <= stail );
      
      // In current step it is required to find command separator: header separator or message terminator.
      // If a command have a data parameter, the parser will find header separator (space), otherwise
      // the parser will find either message terminator (NL) or message separator (semicolon).

      // Declare a set of parser functions to use:
      eScpiParserStatus_t (* x_fParser[2])( xParseEntry_t * xObj ) = 
      {
          parseProgramHeaderSeparator,     // try to find program header separator (HEADER <SEP> DATA)
          parseProgramMessageSeparator     // try to find program message separator (HEADER1 <SEP> HEADER2)
      };
      size_t iParserIdx = 0;               // current parser index

L_fsqf_ProcessCommandHeader_again:         // marker to return

      // prepare parser context
      if( !prepareParserContext( &(global_ctx->sParser.xCtxObj), // re-use @xCtxObj object 
                                 shead,  // pass input buffer
                                  (stail - shead), // pass input buffer size
                                   global_ctx->sMessage.w_bEndOfMessage, // pass end-of-message by identifying the event
                                    private_ctx->bKeepParserContext ) ) // parser context keep indicator
      {
          global_ctx->sEvent.eStatus = eScpiStatus_invalid;

          (void)nextstate; // Note: go to next state: eHandleError (default)
      }
      else
      {
          global_ctx->sEvent.eStatus = eScpiStatus_failed;  // forward set

          // try to call the parser only if index is not out of range
          if( iParserIdx < (sizeof(x_fParser)/sizeof(*x_fParser)) )
          {
              // 1. iParserIdx=0:  parseProgramHeaderSeparator
              // 2. iParserIdx=1:  parseProgramMessageSeparator
              global_ctx->sEvent.eStatus = x_fParser[iParserIdx++]( &(global_ctx->sParser.xCtxObj) );
          }

          // check status:
          switch( global_ctx->sEvent.eStatus )
          {
              // in case success:
              case eScpiStatus_success:
              {
                  // successfully parsed: program command header separator

                  // Retrieve the last parsed entity tail and 
                  // ... update current input buffer iterator @sMessage.pStr
                  getParsedEntityDetails( &(global_ctx->sParser.xCtxObj),
                                  NULL,
                    (void const**)&global_ctx->sMessage.pStr,
                                  NULL );
                  (void)global_ctx->sMessage.pStr;

                  assert( eScpiEventWrite == global_ctx->sEvent.eCode
                       || eScpiEventRead  == global_ctx->sEvent.eCode );

                  assert( eScpiEntityTypeCmnQueryProgHdr   == private_ctx->eCmdHeader_type ||
                          eScpiEntityTypeCmpQueryProgHdr   == private_ctx->eCmdHeader_type ||
                          eScpiEntityTypeCmnCommandProgHdr == private_ctx->eCmdHeader_type ||
                          eScpiEntityTypeCmpCommandProgHdr == private_ctx->eCmdHeader_type );
                  
                  bool eventProcess = false;

                  // decode entity type to encode new event for next state
                  switch( private_ctx->eCmdHeader_type )
                  {
                     // For command-type entities:
                     case eScpiEntityTypeCmnCommandProgHdr:
                     case eScpiEntityTypeCmpCommandProgHdr:
                     {
                          global_ctx->sMessage.bQuery = false; // Command type
                          eventProcess = true;
                     }
                     break;

                     // For query-type entities:
                     case eScpiEntityTypeCmnQueryProgHdr: 
                     case eScpiEntityTypeCmpQueryProgHdr:  
                     {
                          global_ctx->sMessage.bQuery = true; // Query type
                          eventProcess = true;
                     }
                     break;

                     default:  // impossible
                          eventProcess = false;
                  }

                  if( eventProcess )
                  {
                      // Command mnemonic successfully parsed

                      // The command is ready to be executed
                      // Restore parsing iterator to search command parameters
                      global_ctx->sMessage.pStr = (const uint8_t*)global_ctx->sParser.xHandlerToken.stail;

                      // prepare parser context
                      if( !prepareParserContext( &(global_ctx->sParser.xCtxObj), 
                                     global_ctx->sMessage.pStr,  // pass input buffer
                                      (ptrdiff_t)global_ctx->sMessage.pEnd - (ptrdiff_t)global_ctx->sMessage.pStr, // pass input buffer size
                                       global_ctx->sMessage.w_bEndOfMessage, // pass end-of-message by identifying the event
                                        private_ctx->bKeepParserContext ) )
                      {
                          assert(false); // forbidden state

                          global_ctx->sEvent.eStatus = eScpiStatus_failed;

                          // Something went wrong...
                          fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, 
                                      global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );

                          (void)nextstate; // Note: go to next state: eHandleError (default)
                      }
                      else
                      {
                          // Next station: data processing state, be careful, the state is about closing :)
                          // go to next state: eProcessProgramData
                          nextstate = fsq_GetStateById(this,eProcessProgramData);
                      }
                  }
                  else
                  {
                      global_ctx->sEvent.eStatus = eScpiStatus_failed;
 
                      assert(false); // forbidden state

                      // Something went wrong...
                      fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, 
                                      global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
                      
                      (void)nextstate; // Note: go to next state: eHandleError (default)
                  }
              }
              break;

              case eScpiStatus_need_data:
              {
                  private_ctx->bKeepParserContext = true;  // keep parser context til next call

                  nextstate = NULL; // NULL, do not change state
              }
              break;
              
              case eScpiStatus_failed:                     
              case eScpiStatus_invalid:
              default:
              {
                  // try another parser
                  if( iParserIdx < (sizeof(x_fParser)/sizeof(*x_fParser)) )
                  {
                      goto L_fsqf_ProcessCommandHeader_again;
                  }

                  // Enter error state
                  fsq_RaiseError( SCPI_ERROR_COMMAND_HEADER_SEP, SCPI_ERROR_COMMAND_HEADER_SEP_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );

                  (void)nextstate; // Note: go to next state: eHandleError (default)
              }
              break;
          }
      }
    }

L_fsqf_ProcessCommandHeader_EXIT:
    return nextstate;
}
