#ifndef GPIB_PARSER_STRUTILS_H
#define GPIB_PARSER_STRUTILS_H

    #include <string.h>
    #include <stddef.h>  // size_t
    #include <stdint.h>  // int32_t
    #include "gpib_parser_casts.h"

    // STRCACHE_BUFFER_SIZE:
    // The size of statical buffer for GPIB_StrCopyToCache()
    #define STRCACHE_BUFFER_SIZE                             RAM_SCPIBUFFER_SIZE    
    
    BOOL GPIB_StrChkFormat( const char * str, int length, const eStringCheckMode_t mode );     // "gpib_parser_strutils.c"
    
    /* #define strstri(STR1, STR2) strstr((STR1),(STR2))  */
    /* #define strcmpi(STR1, STR2) strcmp((STR1),(STR2))  */

    // strwhite:
    // Looks for a "white" character in the null-terminated string 
    // and returns the pointer to it.
    //
    // Note: the function uses "rw" pointers due to it is required by application
    static inline char * strwhite( char * string )
    {
        // Skip first "white" or end-of-line charater
        while( !IsWhiteOrEndChar( *string ) )
        {  
            string ++;
        }
        
        return string;
    }
    
    // searchHeaderEnd:
    // Looks for the end of header end by checking the following rules:
    //  - if the character is a white, it is considered as the end of header
    //  - if the character is a command separator, it is considered as the end of header
    // Returns the pointer to the end.
    // Note: the function uses "rw" pointers due to it is required by application
    static inline char * searchHeaderEnd( char * string, const char * end )
    {
        // Skip first "white" or end-of-line charater
        while(     
                   !IsWhiteOrEndChar( *string ) 
                && (GPIB_CHAR_CMDSEPARATOR_SIGN != *string) 
                && (end != string)  
             )
        {  
            string ++;
        }
        
        return string;
    }
    
    // strucase:
    // Converts @length characters in the string @str into the HIGH register
    static inline void strucase( char * str, size_t length )
    { 
        if( NULL != str )
        {
            while( length > 0 )
            {
                *str = toupper( *str );
                
                length--;
                
                str++;
            }
        }
    }
    
    // strlcase:
    // Converts @length characters in the string @str into the LOW register
    static inline void strlcase( char * str, size_t length )
    { 
        if( NULL != str )
        {
            while( length > 0 )
            {
                *str = tolower( *str );
                
                length--;
                
                str++;
            }
        }
    }
    

    // GPIB_StrCopyToCache:
    // Caches the given string @source into the internal buffer and returns the new pointer
    char * GPIB_StrCopyToCache( const char * source, size_t length );

    // GPIB_StrChr:
    // Searches a character @chr in the string beginning from @str and limited by @end.
    // If the function faces a null-character or end-of-line condition, it stops searching.
    // Function returns the pointer to the found character in @str, or NULL if not found.
    const char * GPIB_StrChr( const char * str, const char chr, const char * end );
    
    // GPIB_GetStringEnd:
    // Searches for the end of the string @gpibstr until the end of @gpibstr is reached
    // or @length characters is processed 
    // Note: the string must be quotted in single or double quotes.
    const char * GPIB_GetStringEnd( const char * gpibstr, size_t length );

    // GPIB_GetParameterEnd:
    // Searches for the end signature of the parameter in @string until the 
    // end of @string string is reached or @length characters is processed.
    // The first "white" character is treated as an end signature.
    // Does not support SCPI DataBlock format.
    const char * GPIB_GetParameterEnd( const char * string, size_t length );
    
    // GPIB_CheckHeader:
    // Check if the given @header is valid GPIB header.
    // Returns TRUE if it is, and FALSE otherwise.
    //BOOL GPIB_CheckHeader( const char * header );
    BOOL GPIB_CheckHeader( const char * header, const char * end );
    
    // GPIB_HeaderCompare:
    // Compares a couple of GPIB command headers.
    // Note: the comparation is case insensitive.
    // @headerTree - the original command header, including an optional command 
    //   signature (if required), e.g. optional "[SYSTem]" or mandatory "SYST"
    // @headerUser - actually entered command header
    // Note: for optional headers it is not required a paired ']' in the end of header.
    // Returns TRUE if headers are identical, and FALSE otherwise.
    BOOL GPIB_HeaderCompare(  const char * headerTree, const char * headerUser );
    
     // GPIB_StrChr_rw:
    // Searches a character @chr in the string beginning from @str and limited by @end.
    // If the function faces a null-character or end-of-line condition, it stops searching.
    // Function returns the pointer to the found character in @str, or NULL if not found.
    // Note: the same as GPIB_StrChr() but with read-write pointers
    static inline char * GPIB_StrChr_rw( char * str, const char chr, char * end )
    {
        const char * found = GPIB_StrChr( str, chr, end );
        
        if( NULL != found )
        {
            return (str + SubPointers( found, str ));
        }
        
        return NULL;
    }
    
    
    // GPIB_GetStringEnd_rw:
    // Searches for the end of the string @gpibstr until the end of @gpibstr is reached
    // or @length characters is processed 
    // Note: the string must be quotted in single or double quotes.
    // Note: the same as GPIB_GetStringEnd() but with read-write pointers
    static inline char * GPIB_GetStringEnd_rw( char * gpibstr, size_t length )
    {
        const char * found = GPIB_GetStringEnd( gpibstr, length );
        
        if( NULL != found )
        {
            return (gpibstr + SubPointers( found, gpibstr ));
        }
        
        return NULL;
    }
    
    typedef enum
    {
        err_IsDataBlock_Success   = 0,
        err_IsDataBlock_NotFound = -1,
        err_IsDataBlock_Invalid  = -2,
        err_IsDataBlock_Trimmed  = -3,
        err_IsDataBlock_Huge     = -4,        
    }
    err_IsDataBlock_t;
    
    // IsDataBlock:
    // The data transfer block validator: Binary transfer format (IEEE488.2)
    // Format: the data block consist of the following elements: '#', N, Z, DATA
    // '#' - data block signature character;
    // N - a demical digit, amount of digits in Z;
    // Z - a demical number (multi-digit), the size of binary bytes in DATA.
    // DATA - binary data bytes.
    // e.g. "#213Hello, world!"
    //  N=2  - amount of digits in Z      
    //  Z=13 - amount of binary bytes (strlen(DATA)=13)     
    // DATA = "Hello, world!"
    //
    // @begin - the string to validate
    // @length - the length of string @begin
    // @p_nsize - optional pointer which will be filled up with the datablock size in bytes
    // @pError - optional pointer to the error variable to be filled with the error code:
    // (err_IsDataBlock_Success) - success;
    // (err_IsDataBlock_NotFound) - datablock not found; 
    // (err_IsDataBlock_Invalid) - datablock found, but has invalid format
    // (err_IsDataBlock_Trimmed) - datablock found, but it is trimmed
    // (err_IsDataBlock_Huge) - datablock found, but it's too large
    //
    // Returns TRUE only if the datablock format is valid, all the parameter of datablock
    // are valid and do not violates any ranges.
    BOOL IsDataBlock( const char * begin, size_t length, size_t * p_nsize, int * pError );
#endif