#define SCPI_CORE_STATE__SEARCH4COMMAND_GUARD
#include "app/scpi/CommandParserStates/search_for_command.h"
#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#include "app/scpi/scpi_core_private.h"

// =================================================================================
// @sSearchForCommandHeaderContext_t
// State's private context typedef
typedef struct
{ 
   bool bKeepParserContext;
}
sSearchForCommandHeaderContext_t;

// =================================================================================
// @ctx_SearchForCommandHeader
// State's private context
sSearchForCommandHeaderContext_t ctx_SearchForCommandHeader;

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

static void fsqe_SearchForCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static void fsql_SearchForCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static const struct fFSeqEntry_t * fsqf_SearchForCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pnext );

const fFSeqVTable_t fsqvbl_SearchForCommandHeader = 
{
     .f = fsqf_SearchForCommandHeader,
     .enter = fsqe_SearchForCommandHeader,  
     .leave = fsql_SearchForCommandHeader
};

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

    private_ctx->bKeepParserContext = false;
    (void)global_ctx;
}

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

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

// =================================================================================
// @fsqf_SearchForCommandHeader
// State's body routine
static const struct fFSeqEntry_t * fsqf_SearchForCommandHeader( 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;
    sSearchForCommandHeaderContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
    // --------------------------------------------------------------------------

    // Handle events:
    switch( global_ctx->sEvent.eCode )
    {    
        case eScpiEventRestart:
        {
             nextstate = fsq_GetStateById(this,eParserResetState);
        }
        break;

        case eScpiEventWrite:          // Handle the WRITE event
        case eScpiEventRead:          // Handle the READ event
        {
             // 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 ) )
             {
                 global_ctx->sEvent.eStatus = eScpiStatus_invalid;
             }
             else
             {
                 // parse the command program header
                 global_ctx->sEvent.eStatus = parseCommandProgramHeader( &(global_ctx->sParser.xCtxObj) );

                 switch( global_ctx->sEvent.eStatus )
                 {
                     case eScpiStatus_success:
                     {
                         // successfully parsed

                         // go to next state: eProcessCommandHeader
                         nextstate = fsq_GetStateById(this,eProcessCommandHeader);
                     }
                     break;

                     case eScpiStatus_need_data:
                     {
                         private_ctx->bKeepParserContext = true;

                         (void)nextstate; // keep NULL, do not change state
                     }
                     break;

                     
                     case eScpiStatus_failed:                     
                     case eScpiStatus_invalid:
                     {
                         const char * start = NULL;
                         const char * stop = NULL;
                         uint8_t type;
                         
                         if( getParsedEntityDetails( &(global_ctx->sParser.xCtxObj), (const void**)&start, (const void**)&stop, &type ) )
                         {
                             // check for empty-line command string
                             if( scpi_isnl(*start) )  
                             {
                                 // requested string is an empty-line command string, and the parser error is obviously can not be generated
                                 // But it is required to enter the error state because only the error state is legal next state.
                                 // It is impossible to restart the parser because maybe there are some data in output buffer to sent.
                             }
                             else
                             {
                                 // Check if the last character is '\n' and cut it off
                                 if( scpi_isnl((*(stop-1))) && (0 < (stop - start)) ) stop--;

                                 int32_t error;
                                 const char * msg = getParserError( &(global_ctx->sParser.xCtxObj), &error ); 

                                 if( NULL == msg )
                                 {
                                     error = SCPI_ERROR_COMMAND_HEADER;
                                     msg = SCPI_ERROR_COMMAND_HEADER_MSG;
                                 }

                                 fsq_RaiseError( error, msg, start, stop );
                             }
                         }
                         else
                         {
                         #if 0
                             int32_t error;
                             const char * msg = getParserError( &(global_ctx->sParser.xCtxObj), &error ); 

                             if( NULL == msg )
                             {
                                 error = SCPI_ERROR_COMMAND_HEADER;
                                 msg = SCPI_ERROR_COMMAND_HEADER_MSG;
                             }

                             fsq_RaiseError( error, msg, NULL, 0 );
                         #else
                             assert( false );
                         #endif
                         }
                         
                         nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
                     }
                     break;
                     
                 }
             }
        }
        break;

        case eScpiEventError:         // Transport error.
        {
            // Enter error state
            nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
        }
        break;
    }

    (void)global_ctx->sEvent.eCode; // keep event code, it will be reused in @eProcessProgramData state
    return nextstate;
}
