#define SCPI_ARGS_HELPER_C
#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#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" // processCharacterArgument
#include "app/scpi/scpi_numeric.h" // processNumericArgument
#include "app/scpi/scpi_args_helper.h"
#include "app/fseq/fseq.h" // tFSeqCtx_t
#include "app/scpi/CommandParserStates/process_data.h" // sProcessProgramDataCommonContext_t

#include "my_assert.h"

// [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

// @parseArguments_helper
// Helper function. Used for convenient call of @parseArguments inside command handler module
// Parameters:
// @common_ctx - process program data state context, see @sProcessProgramDataCommonContext_t
// @argTypes - array of argument types to set the parameters parsing methods (see @parseArguments, @eScpiArgType_t)
// Other parameters: refer @parseArguments
// Returns: refer @parseArguments 
eScpiParserStatus_t parseArguments_helper( void * common_ctx, const uint8_t * argTypes, size_t argN, size_t argOptionalN)
{
   assert( argN <= SCPI_MAX_ARGS );
   
   sProcessProgramDataCommonContext_t * _common_ctx = common_ctx;
   sScpiParserContext_t * global_ctx = _common_ctx->global_ctx;

   // parse command parameters
   eScpiParserStatus_t status = parseArguments( &(global_ctx->sParser.xCtxObj), _common_ctx->argTokens, argTypes, _common_ctx->argEntityTypes, argN, argOptionalN, &_common_ctx->args );

   // Modify state status value
   // This status can be used inside Dispatch routine of command handler
   _common_ctx->argsParserStatus = status;

   // check the parser status result
   if( eScpiParserStatus_success != status )
   {
       // _common_ctx->args is index of parameter
       assert( _common_ctx->args < SCPI_MAX_ARGS );

       // assign whole arguments chain token
       global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[_common_ctx->args].shead;
       global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[_common_ctx->args].stail;
   }
   else
   {
       // _common_ctx->args is amount of parameters
       assert( _common_ctx->args <= SCPI_MAX_ARGS );

       // assign whole arguments chain token
       global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[0].shead;
       global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[_common_ctx->args - 1].stail;
   }

   // Reset indicator: end-of-command has been parsed
   _common_ctx->needParseEndOfCommand = false;

   return status;
}

// [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

// @parseArrayArguments_helper
// Helper function. Used for convenient call of @parseArguments inside command handler module
// Parameters:
// @common_ctx - process program data state context, see @sProcessProgramDataCommonContext_t
// @argTypes - array of argument types to set the parameters parsing methods (see @parseArguments, @eScpiArgType_t)
// Other parameters: refer @parseArguments
// Returns: refer @parseArguments 
eScpiParserStatus_t parseArrayArguments_helper( void * common_ctx, const uint8_t * argTypes, size_t argN, size_t argOptionalN)
{
   assert( argN <= SCPI_MAX_ARGS );
   
   sProcessProgramDataCommonContext_t * _common_ctx = common_ctx;
   sScpiParserContext_t * global_ctx = _common_ctx->global_ctx;

   size_t argNumber = sizeof(_common_ctx->argArray)/sizeof(_common_ctx->argArray[0]);
   // parse command parameters
   eScpiParserStatus_t status = parseArrayArguments( &(global_ctx->sParser.xCtxObj), _common_ctx->argArray, &argNumber, argTypes, _common_ctx->argEntityTypes, argN, argOptionalN, &_common_ctx->args );

   // Modify state status value
   // This status can be used inside Dispatch routine of command handler
   _common_ctx->argsParserStatus = status;

   // check the parser status result
   if( eScpiParserStatus_success != status )
   {
       // _common_ctx->args is index of parameter
       //assert( _common_ctx->args < SCPI_MAX_ARGS );

       // assign whole arguments chain token
       global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[_common_ctx->args].shead;
       global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[_common_ctx->args].stail;
   }
   else
   {
       // _common_ctx->args is amount of parameters
       //assert( _common_ctx->args <= SCPI_MAX_ARGS );

       // assign whole arguments chain token
       global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[0].shead;
       global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[_common_ctx->args - 1].stail;
   }

   // Reset indicator: end-of-command has been parsed
   _common_ctx->needParseEndOfCommand = false;

   return status;
}

// @processArgument_Character_helper
// Helper function. Used for convenient call of the other helper functions.
// Processes the CHARACTER argument. See @processCharacterArgument
// Parameters:
// @common_ctx - process program data state context, see @sProcessProgramDataCommonContext_t
// @argTypes - array of argument types to set the parameters parsing methods (see @parseArguments, @eScpiArgType_t) 
// Other parameters: refer @processCharacterArgument
// Returns: SCPI_ARGUMENT_CHARACTER_INVALID_ID in error case, or argument option index in @allowedList
int8_t processArgument_Character_helper( void * common_ctx, const uint8_t * argTypes, const xArgument_t * allowedList, uint8_t argId )
{
    assert( common_ctx );
    assert( argTypes );

    if( NULL == common_ctx || NULL == argTypes )
    {
        return SCPI_ARGUMENT_CHARACTER_INVALID_ID;
    }

    assert( argTypes[ argId ] == eScpiArg_Character );

    // analyze argument type
    if( argTypes[ argId ] != eScpiArg_Character )
    {
        return SCPI_ARGUMENT_CHARACTER_INVALID_ID;
    }

    bool state = false;
    int8_t argument_idx = 0;
    sProcessProgramDataCommonContext_t * _common_ctx = common_ctx;
    sScpiParserContext_t * global_ctx = _common_ctx->global_ctx;

    state = processCharacterArgument( allowedList, &_common_ctx->argTokens[argId], &argument_idx );
    
    // assign current argument token (success or failed)
    global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[argId].shead;
    global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[argId].stail;

    if( ! state )
    {
        argument_idx = SCPI_ARGUMENT_CHARACTER_INVALID_ID;
    }

    return argument_idx;
}

// @processArgument_Numeric_helper
// Helper function. Used for convenient call of the other helper functions.
// Processes the NUMERIC argument. See @processNumericArgument
// Parameters:
// @common_ctx - process program data state context, see @sProcessProgramDataCommonContext_t
// @argTypes - array of argument types to set the parameters parsing methods (see @parseArguments, @eScpiArgType_t) 
// Other parameters: refer @processNumericArgument
// Returns: pointer to the numeric entry descriptor with value and type, and the error code, or NULL
// if @common_ctx is set to NULL.
const sNumericEntry_t * processArgument_Numeric_helper( void * common_ctx, const uint8_t * argTypes, const sNumericRange_t * range, uint8_t argId )
{
    assert( common_ctx );
    assert( argTypes );

    sProcessProgramDataCommonContext_t * _common_ctx = common_ctx;

    if( NULL == common_ctx )
    {
        (void)_common_ctx->numericProcessContext.valueEntry.error; // inaccessible
        assert(false);
        return NULL;
    }
    else
    if( NULL == argTypes )
    {
        _common_ctx->numericProcessContext.valueEntry.error = ScpiNumericInvalidArg;
        assert(false);
    }
    else
    {
        // analyze argument type
        switch( argTypes[ argId ] )
        {
           case eScpiArg_Numeric:
           case eScpiArg_Numeric_Demical:
           case eScpiArg_Numeric_NonDemical:
           case eScpiArg_Array_Decimal:
           {
                bool state = false;

                sScpiParserContext_t * global_ctx = _common_ctx->global_ctx;
                
                _common_ctx->numericProcessContext.valueEntry.error = ScpiNumericInvalidArg;
                
                state = processNumericArgument( &_common_ctx->numericProcessContext,
                                                &_common_ctx->argTokens[argId],
                                                _common_ctx->argEntityTypes[argId],
                                                range ); 
                
                // assign current argument token (success or failed)
                global_ctx->sParser.xArgToken.shead = _common_ctx->argTokens[argId].shead;
                global_ctx->sParser.xArgToken.stail = _common_ctx->argTokens[argId].stail;

                if( !state )
                {(void)_common_ctx->numericProcessContext.valueEntry.error;}
           }
           break;
           default:
              _common_ctx->numericProcessContext.valueEntry.error = ScpiNumericInvalidArg; // invalid type
              assert(false); 
        }
    }

    // error code set helper
    switch( _common_ctx->numericProcessContext.valueEntry.error )
    {
       case ScpiNumericSuccess:
       {
            (void)_common_ctx->status; // caller dependent
       }
       break;

       // "11.5.1.1.5", [1]
       case ScpiNumericError_DEVICE_RANGE:   // the value is out of range supported by device
       {
            _common_ctx->status = eProgramDataArgumentRange; // parameter value range error, caller should generate error message
       }  
       break;

       // "11.5.1.1.5", [1]
       // "10.10.6 Error Handling", [1]
       // "<...> An out-of-range integer shall cause an Execution Error, see 11.5.1.1.5. <...>"
       case ScpiNumericError_USER_RANGE:     // the value is out of user specified range:   // the value is out of range supported by device
       {
            _common_ctx->status = eProgramDataArgumentValue; // parameter value range error, caller should generate error message
       }  
       break;

       case ScpiNumericError_USER_TYPE:      // the value does not match to the user expectation
       {
            _common_ctx->status = eProgramDataArgumentType; // parameter type error, caller should generate error message
       }
       break;

       case ScpiNumericInvalidArg: assert(false); // forbidden case: design bug                      
       case ScpiNumericError:                     // generic numeric conversion error
       {
            _common_ctx->status = eProgramDataRuntimeError; // parameter syntax error, caller should generate error message
       }
       break;
    }

    return &_common_ctx->numericProcessContext.valueEntry;
}

const sNumericEntry_t * processArgument_Array_helper( void * common_ctx, const sNumericRange_t * range, uint8_t argId, void * result_arg, uint16_t result_size)
{  
    assert( common_ctx );
    
//    sProcessProgramDataCommonContext_t * _common_ctx = common_ctx;
//    uint16_t * _result_arg = result_arg;
//    for(uint8_t i = 0; i < _common_ctx->args; i++)
//    {
//        bool state = processNumericArgument( &_common_ctx->numericProcessContext,
//                                             &_common_ctx->argArray[i],
//                                             _common_ctx->argEntityTypes[0],
//                                             range );
//        if(state && i < result_size)
//        {
//            *_result_arg = _common_ctx->numericProcessContext.valueEntry.Value.demicalInteger;
//            _result_arg++;
//        }
//        else
//        {
//            _common_ctx->status = eProgramDataRuntimeError; // parameter syntax error, caller should generate error message
//        }
//    }
    sNumericEntry_t zagl;
    return &zagl;
}

// @scpi_argument_value_token
// Get the string tokens of the specified value in the allowed CHARACTER value list.
// Parameters:
// @list - allowed values list, see @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST
// @valueIndex - index in the array;
// @pOutToken - string token to fill, can not be NULL;
// Returns: filled string token (@pOutToken).
// Note: if @valueList is out of range, NULL pointer is returned, and @pOutToken is unchanged.
const sStrToken_t * scpi_argument_value_token( const xArgument_t * list, size_t valueIndex, sStrToken_t * pOutToken )
{
   if( NULL == list || NULL == pOutToken ) return NULL;

   // walk in the list
   while( (*list != NULL) && (valueIndex>0) )
   {
      valueIndex--; list++;
   }

   if( NULL == *list ) return NULL;

   pOutToken->shead = *list;
   pOutToken->stail = strlen(*list) + *list;

   return pOutToken;
}