#ifndef SCPI_ARGS_HELPER_H
#define SCPI_ARGS_HELPER_H
  // SCPI Command Argument parser helper header.
  // Include only to the command handler file like 'idn.c' or similar.
  // ------------------------------------------------------------------------
  #include "app/scpi/scpi_base.h" // SCPI_MAX_ARGS
  #include "app/scpi/scpi_commands.h" // processCharacterArgument
  #include "app/scpi/scpi_numeric_types.h" // sProcessNumericEntry_t, sStrToken_t, sNumericRange_t
  #include "app/scpi/scpi_numeric.h"
  #include "app/scpi/scpi_args.h"
  #include <stdint.h> // uint8_t
  // ------------------------------------------------------------------------
  // @SCPI_ARGS_N macro:
  // Defines amount of SCPI-command arguments including optional ones.
  // The command handler module must include this header either after defining this macro,
  // or after defining a pair of SCPI_ARGS_N_Q + SCPI_ARGS_N_C.
  //
  // @SCPI_ARGS_N_Q macro:
  // Defines amount of SCPI-command arguments including optional ones for QUERY.
  // The command handler module must include this header after defining this macro.
  // It is required to be defined together with SCPI_ARGS_N_C.
  // Useless if SCPI_ARGS_N is defined.
  //
  // @SCPI_ARGS_N_C macro:
  // Defines amount of SCPI-command arguments including optional ones for COMMNAD(non-query).
  // The command handler module must include this header after defining this macro.
  // It is required to be defined together with SCPI_ARGS_N_Q.
  // Useless if SCPI_ARGS_N is defined.

  // @SCPI_ARGS_MANDATORY_N macro:
  // Defines amount of mandatory SCPI-command arguments, not including optional ones.
  // If the command requires all the parameters and does not allow to omit any argument,
  // this value must be equal to SCPI_ARGS_N, or must be undefined. The command handler module
  // must include this header after defining this macro. All the optional command parameters
  // are located in the end of the argument set.
  // The command handler module must include this header either after defining this macro,
  // or after defining a pair of SCPI_ARGS_MANDATORY_N_Q + SCPI_ARGS_MANDATORY_N_C.

  // @SCPI_ARGS_MANDATORY_N_Q macro:
  // Defines amount of mandatory SCPI-command arguments, not including optional ones, for QUERY.
  // If the command requires all the parameters and does not allow to omit any argument,
  // this value must be equal to SCPI_ARGS_N_Q, or must be undefined. The command handler module
  // must include this header after defining this macro. All the optional command parameters are
  // located in the end of the argument set.
  // It is required to be defined together with SCPI_ARGS_MANDATORY_N_C.
  // Useless if SCPI_ARGS_MANDATORY_N is defined.

  // @SCPI_ARGS_MANDATORY_N_C macro:
  // Defines amount of mandatory SCPI-command arguments, not including optional ones, for COMMAND.
  // If the command requires all the parameters and does not allow to omit any argument,
  // this value must be be equal to SCPI_ARGS_N_C, or must be undefined. The command handler module
  // must include this header after defining this macro. All the optional command parameters are
  // located in the end of the argument set.
  // It is required to be defined together with SCPI_ARGS_MANDATORY_N_Q.
  // Useless if SCPI_ARGS_MANDATORY_N is defined.

  #define GC(_common_ctx_) ((sScpiParserContext_t*)((_common_ctx_)->global_ctx))

  #if !defined(SCPI_ARGS_HELPER_C)
  #ifndef SCPI_ARGS_N
  #if    defined(SCPI_ARGS_N_Q) && !defined(SCPI_ARGS_N_C)
  #error The macro 'SCPI_ARGS_N_C' is undefined. 
  #elif  defined(SCPI_ARGS_N_C) && !defined(SCPI_ARGS_N_Q)
  #error The macro 'SCPI_ARGS_N_Q' is undefined. 
  #elif !defined(SCPI_ARGS_N_C) && !defined(SCPI_ARGS_N_Q)
  #error The macro 'SCPI_ARGS_N' shall be defined to the amount of command parameters or 'SCPI_ARGS_N_Q' and 'SCPI_ARGS_N_C' shall be used instead.
  #endif
  #endif
  #if defined(SCPI_ARGS_N) && (SCPI_ARGS_N > SCPI_MAX_ARGS)
  #error Invalid value 'SCPI_ARGS_N'. Limit exceeded. See the limit 'SCPI_MAX_ARGS'.
  #endif
  #if defined(SCPI_ARGS_N_Q) && (SCPI_ARGS_N_Q > SCPI_MAX_ARGS)
  #error Invalid value 'SCPI_ARGS_N_Q'. Limit exceeded. See the limit 'SCPI_MAX_ARGS'.
  #endif
  #if defined(SCPI_ARGS_N_C) && (SCPI_ARGS_N_C > SCPI_MAX_ARGS)
  #error Invalid value 'SCPI_ARGS_N_C'. Limit exceeded. See the limit 'SCPI_MAX_ARGS'.
  #endif
  #if defined(SCPI_ARGS_N)
  #ifndef SCPI_ARGS_MANDATORY_N
  #define SCPI_ARGS_MANDATORY_N SCPI_ARGS_N
  #elif SCPI_ARGS_MANDATORY_N > SCPI_ARGS_N
  #error The value 'SCPI_ARGS_MANDATORY_N' shall not be greater than 'SCPI_ARGS_N'
  #endif
  #endif
  #if defined(SCPI_ARGS_N_Q) && defined(SCPI_ARGS_N_C)
  #ifndef SCPI_ARGS_MANDATORY_N_C
  #define SCPI_ARGS_MANDATORY_N_C SCPI_ARGS_N_C
  #elif SCPI_ARGS_MANDATORY_N_C > SCPI_ARGS_N_C
  #error The value 'SCPI_ARGS_MANDATORY_N_C' shall not be greater than 'SCPI_ARGS_N_C'
  #endif
  #ifndef SCPI_ARGS_MANDATORY_N_Q
  #define SCPI_ARGS_MANDATORY_N_Q SCPI_ARGS_N_Q
  #elif SCPI_ARGS_MANDATORY_N_Q > SCPI_ARGS_N_Q
  #error The value 'SCPI_ARGS_MANDATORY_N_Q' shall not be greater than 'SCPI_ARGS_N_Q'
  #endif
  #endif
  #else // scpi_args_helper.c
  
  #endif
  // ------------------------------------------------------------------------
  // @SCPI_ARGS_VAR_TOKENS
  // Default variable name for the string tokens array
  #define SCPI_ARGS_VAR_TOKENS argTokens

  #if defined(SCPI_ARGS_N)
  // @SCPI_ARGS_VAR_TYPES
  // Default variable name for the argument types array
  #define SCPI_ARGS_VAR_TYPES  argTypes
  #endif

  #if defined(SCPI_ARGS_N_C) && defined(SCPI_ARGS_N_Q)
  // @SCPI_ARGS_VAR_TYPES_C
  // Default variable name for the argument types array (command only)
  #define SCPI_ARGS_VAR_TYPES_C  argTypesCommand

  // @SCPI_ARGS_VAR_TYPES_Q
  // Default variable name for the argument types array (query only)
  #define SCPI_ARGS_VAR_TYPES_Q  argTypesQuery
  #endif
  // ------------------------------------------------------------------------
  // @DECLARE_SCPI_ARGS macro:
  // Use this macro to decalre parser entities for command arguments.  
  // @_VariableTypes_ - the name of the argument types array
  // The rest arguments of this macro are the set of SCPI_ARGS_N types of command 
  // arguments (eScpiArgType_t), e.g.:
  //  #define SCPI_ARGS_N 1
  //  DECLARE_SCPI_ARGS( SCPI_ARGS_VAR_TYPES, eScpiArg_Character );  
  #if defined(SCPI_ARGS_N)
  #if SCPI_ARGS_N == 0
  #define DECLARE_SCPI_ARGS(...)
  #else
  #define DECLARE_SCPI_ARGS(...)  \
          static const uint8_t SCPI_ARGS_VAR_TYPES[ SCPI_ARGS_N ] = { __VA_ARGS__ };
  #endif
  #elif defined(SCPI_ARGS_N_C) && defined(SCPI_ARGS_N_Q)
  #if SCPI_ARGS_N_C == 0
  #define DECLARE_SCPI_ARGS_C(...)
  #else  
  #define DECLARE_SCPI_ARGS_C(...)  \
          static const uint8_t SCPI_ARGS_VAR_TYPES_C[ SCPI_ARGS_N_C ] = { __VA_ARGS__ };
  #endif
  #if SCPI_ARGS_N_Q == 0
  #define DECLARE_SCPI_ARGS_Q(...)
  #else
  #define DECLARE_SCPI_ARGS_Q(...)  \
          static const uint8_t SCPI_ARGS_VAR_TYPES_Q[ SCPI_ARGS_N_Q ] = { __VA_ARGS__ };
  #endif
  #endif

  // Routine @parseArguments call helper
  #if defined(SCPI_ARGS_N)
  #define SCPI_PARSE_ARGUMENTS_C(_common_ctx_)  (assert(false), 0)
  #define SCPI_PARSE_ARGUMENTS_Q(_common_ctx_)  (assert(false), 0)
  #if SCPI_ARGS_N == 0
  #define SCPI_PARSE_ARGUMENTS(_common_ctx_)    parseArguments_helper( (_common_ctx_), NULL, SCPI_ARGS_N, 0 )
  #else
  #define SCPI_PARSE_ARGUMENTS(_common_ctx_)    parseArguments_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES, SCPI_ARGS_N, (SCPI_ARGS_N) - (SCPI_ARGS_MANDATORY_N) )
  #define SCPI_PARSE_ARRAY_ARGUMENTS(_common_ctx_)    parseArrayArguments_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES, SCPI_ARGS_N, SCPI_ARGS_MANDATORY_N )
  #endif
  #elif defined(SCPI_ARGS_N_C) && defined(SCPI_ARGS_N_Q)
  #define SCPI_PARSE_ARGUMENTS(_common_ctx_)    ((GC(_common_ctx_)->sMessage.bQuery)?\
                                                SCPI_PARSE_ARGUMENTS_Q(_common_ctx_):\
                                                SCPI_PARSE_ARGUMENTS_C(_common_ctx_))
  #if SCPI_ARGS_N_C == 0 
  #define SCPI_PARSE_ARGUMENTS_C(_common_ctx_)  parseArguments_helper( (_common_ctx_), NULL, SCPI_ARGS_N_C, 0 )
  #else
  #define SCPI_PARSE_ARGUMENTS_C(_common_ctx_)  parseArguments_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_C, SCPI_ARGS_N_C, (SCPI_ARGS_N_C) - (SCPI_ARGS_MANDATORY_N_C) )
  #endif
  #if SCPI_ARGS_N_Q == 0
  #define SCPI_PARSE_ARGUMENTS_Q(_common_ctx_)  parseArguments_helper( (_common_ctx_), NULL, SCPI_ARGS_N_Q, 0 )
  #else
  #define SCPI_PARSE_ARGUMENTS_Q(_common_ctx_)  parseArguments_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_Q, SCPI_ARGS_N_Q, (SCPI_ARGS_N_Q) - (SCPI_ARGS_MANDATORY_N_Q) )
  #endif
  #endif

  // ------------------------------------------------------------------------
  // @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST
  // Declares an array of elements for SCPI Character Parameter value allowed list
  // Use this macro together with @processCharacterArgument to interpretate passed parameter.
  // @_name_ - variable name
  // @... - rest arguments is a "strings" that contain allowed values, e.g. "NORMal"
  #define DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST(_name_, ...) static const xArgument_t _name_[] = { __VA_ARGS__, NULL }

  // @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_EXPORT - the same as @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST but declare
  // global variable that can be imported to another module.
  #define DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_EXPORT(_name_, ...) const xArgument_t _name_[] = { __VA_ARGS__, NULL }

  // @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_IMPORT
  // Imports an array of elements for SCPI Character Parameter value allowed list that have been
  // declared with @DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_EXPORT in other module.
  // Use this macro together with @processCharacterArgument to interpretate passed parameter.
  #define DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_IMPORT(_name_)      extern const xArgument_t _name_[];

  #define SCPI_ARGUMENT_CHARACTER_VALUE_TOKEN(_list_,_valueN_,_token_) scpi_argument_value_token(_list_,_valueN_,_token_)
  // ------------------------------------------------------------------------
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_F64
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_F64(_name_, _min_, _max_)   static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_F64, .min.f = (_min_), .max.f = (_max_) }
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_F32
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_F32(_name_, _min_, _max_)   static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_F32, .min.f = (_min_), .max.f = (_max_) }
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_I64
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_I64(_name_, _min_, _max_)   static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_I64, .min.i = (_min_), .max.i = (_max_) }
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_I32
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_I32(_name_, _min_, _max_)   static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_I32, .min.i = (_min_), .max.i = (_max_) }  
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_I16
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_I16(_name_, _min_, _max_)   static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_I16, .min.i = (_min_), .max.i = (_max_) }  
  // @DECLARE_ARGUMENT_NUMERIC_VALUES_I8
  //
  #define DECLARE_ARGUMENT_NUMERIC_VALUES_I8(_name_, _min_, _max_)    static const sNumericRange_t _name_ = { .type = SCPI_NUMERIC_LIMIT_TYPE_I8 , .min.i = (_min_), .max.i = (_max_) }  
  // ------------------------------------------------------------------------
  // Routine @processCharacterArgument call helper
  // * Macro group @SCPI_PROCESS_ARGUMENT_NUMERIC, @SCPI_PROCESS_ARGUMENT_C_NUMERIC, @SCPI_PROCESS_ARGUMENT_Q_NUMERIC
  // Process the numeric argument token and convert to the integer or float representation.
  // The result is the pointer to [sNumericEntry_t] structure, where the result is stored together the
  // value type and conversion error result.
  #if defined(SCPI_ARGS_N)
  #define SCPI_PROCESS_ARGUMENT_C_CHARACTER(_common_ctx_, _list_, _id_)    (assert(false), 0)
  #define SCPI_PROCESS_ARGUMENT_Q_CHARACTER(_common_ctx_, _list_, _id_)    (assert(false), 0)
  #define SCPI_PROCESS_ARGUMENT_C_NUMERIC(_common_ctx_, _range_, _id_)     (assert(false), 0)
  #define SCPI_PROCESS_ARGUMENT_Q_NUMERIC(_common_ctx_, _range_, _id_)     (assert(false), 0)
  #if SCPI_ARGS_N == 0                                                     
  #define SCPI_PROCESS_ARGUMENT_CHARACTER(_common_ctx_, _list_, _id_)      (SCPI_ARGUMENT_CHARACTER_INVALID_ID)
  #define SCPI_PROCESS_ARGUMENT_NUMERIC(_common_ctx_, _range_, _id_)       (false)
  #define SCPI_PROCESS_ARGUMENT_NUMERIC(_common_ctx_, _range_, _id_)       (false)
  #define SCPI_PROCESS_ARGUMENT_ARRAY(_common_ctx_, _range_, _id_)         (SCPI_ARGUMENT_CHARACTER_INVALID_ID)
  #else
  #define SCPI_PROCESS_ARGUMENT_CHARACTER(_common_ctx_, _list_, _id_)      processArgument_Character_helper(   (_common_ctx_), SCPI_ARGS_VAR_TYPES, _list_, _id_ )
  #define SCPI_PROCESS_ARGUMENT_NUMERIC(_common_ctx_, _range_, _id_)       processArgument_Numeric_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES, _range_, _id_)
  #define SCPI_PROCESS_ARGUMENT_ARRAY(_common_ctx_, _range_, _id_, _result_arg_, _result_size_) processArgument_Array_helper(   (_common_ctx_), _range_, _id_, _result_arg_, _result_size_)
  #endif
  #elif defined(SCPI_ARGS_N_C) && defined(SCPI_ARGS_N_Q)
  #define SCPI_PROCESS_ARGUMENT_CHARACTER(_common_ctx_, _list_, _id_)      ((GC(_common_ctx_)->sMessage.bQuery)?\
                                                                           SCPI_PROCESS_ARGUMENT_Q_CHARACTER(_common_ctx_,_list_,_id_):\
                                                                           SCPI_PROCESS_ARGUMENT_C_CHARACTER(_common_ctx_,_list_,_id_))
  #define SCPI_PROCESS_ARGUMENT_NUMERIC(_common_ctx_, _range_, _id_)       ((GC(_common_ctx_)->sMessage.bQuery)?\
                                                                           SCPI_PROCESS_ARGUMENT_Q_NUMERIC(_common_ctx_,_range_,_id_):\
                                                                           SCPI_PROCESS_ARGUMENT_C_NUMERIC(_common_ctx_,_range_,_id_))
  #if SCPI_ARGS_N_C == 0 
  #define SCPI_PROCESS_ARGUMENT_C_CHARACTER(_common_ctx_, _list_, _id_)    (SCPI_ARGUMENT_CHARACTER_INVALID_ID)
  #define SCPI_PROCESS_ARGUMENT_C_NUMERIC(_common_ctx_,  _range_, _id_)    (false)
  #else
  #define SCPI_PROCESS_ARGUMENT_C_CHARACTER(_common_ctx_, _list_, _id_)    processArgument_Character_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_C, _list_, _id_ )
  #define SCPI_PROCESS_ARGUMENT_C_NUMERIC(_common_ctx_, _range_, _id_)     processArgument_Numeric_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_C, _range_, _id_ )
  #endif
  #if SCPI_ARGS_N_Q == 0 
  #define SCPI_PROCESS_ARGUMENT_Q_CHARACTER(_common_ctx_, _list_, _id_)    (SCPI_ARGUMENT_CHARACTER_INVALID_ID)
  #define SCPI_PROCESS_ARGUMENT_Q_NUMERIC(_common_ctx_, _range_, _id_)     (false)
  #else
  #define SCPI_PROCESS_ARGUMENT_Q_CHARACTER(_common_ctx_, _list_, _id_)    processArgument_Character_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_Q, _list_, _id_ )
  #define SCPI_PROCESS_ARGUMENT_Q_NUMERIC(_common_ctx_, _range_, _id_)     processArgument_Numeric_helper( (_common_ctx_), SCPI_ARGS_VAR_TYPES_Q, _range_, _id_ )
  #define SCPI_PROCESS_ARGUMENT_Q_ARRAY(_common_ctx_, _range_, _id_)       processArgument_Numeric_helper(   (_common_ctx_), SCPI_ARGS_VAR_TYPES_Q, _range_, _id_ )
  #endif
  #endif

  #define SCPI_ARGUMENT_CHARACTER_INVALID_ID (-1)

  // @processArgument_Character_helper
  // Helper function. Used for convenient call of the other helper functions
  // 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 );

  // @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 );

  const sNumericEntry_t * processArgument_Array_helper( void * common_ctx, const sNumericRange_t * range, uint8_t argId, void * result_arg, uint16_t result_size);
  // @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 );
#endif