#include <stdio.h>
#include "gpib_parser_casts.h"
#include "gpib_parser_validators_numbers.h" // Validators for numbers
#include "gpib_parser_chars.h"              // Service characters definitions
#include "gpib_parser_validators.h"
#include "gpib_parser_numutils.h"
#include <math.h>
// -----------------------------------------------------------------------------
// GPIB_ParseNumbers parses a string and retrieves numbers
// E.g. it processes the following strings: "100,    5", "  10 ,  5 ", " 1, 0" 
//
//            +----<-<-<-------------------<-<-<--------------------------<-<-<---+      
//            |                                                                   |
//            |    {next}                     {next}                              |
//            +----<-<-<----+            +-----<-<-<-----+                        |
//            |             |            |               |                        |
//            |  +-------+  |  {number?} |   +--------+  |  {end?}     +-------+  |                  
// (string)->-+->| space |--+------+-----+-->| number |--+----+->--+-->| comma |--+                
//            |  +-------+  |      |{yes}|   +--------+  |    |    |   +-------+                 
//            |             |      |     |               |    |    | 
//            +---->->->----+      |     +----->->->-----+    |    +--->->->--(exit)
//               {no space}        |     {no more numbers}    |     {no comma}
//                            {no} |                          | 
//                                 +-->->->--(error exit}     +--->->->--(success exit)
//                                  {no number}                {end}
//
// Note: GPIB standard assumes the string ends with end-of-line character ('\n')
// 
// @str - specifies the string to be parsed
// @type - specifies the parsing mode: float or integer
// @count - specifies the exact number of the expecting numbers in the string.
// Note: you must provide enough pointers after @pvalue1 to store all the numbers
//
// Return value: in case of error - negative error code
//               in case of success - zero
//               in case of warning - positive code which means amount of remaining unparsed numbers 
int GPIB_ParseNumbers( const char * str, eGPIB_ParseNumbers_type_t type, size_t count, int * pAnchorValue, ... )
{ 
    int rval = 0;
    
    size_t nsys = 0;    // numeric system code: none(0), bin(2), octal(8), dec(10), hex(16)
    
    float * * pointer_to_pointer_to_float = ( (float**) &pAnchorValue );
    int   * * pointer_to_pointer_to_int   = ( (int**)   &pAnchorValue );
    
    BOOL bExpectNumber = TRUE;         // @bExpectNumber: the flag telling that the next character must be a part of number
    
    // Let's the parse begins!
    
    // Til the end of string and while @count is greater than zero
    while( !IsEndOfLine( *str ) && (count > 0) )
    {
        // Check for "white" character
        if( IsWhiteChar( *str ))
        {
            // Skip it and check the next character
            str++;
            continue;
        }
        
        // the character is not "white" for sure since we skipped all the "white" chars.
       
        // check for the numbers separator (by default is comma)
        if( GPIB_CHAR_NUMSEPARATOR_SIGN == *str )
        {
            if( bExpectNumber )
            {
                // error: while a part of the number expected the separator faced
                rval = -1;
                break;
            }
            else
            {
                (void)nsys;     // do not change @nsys
                
                str++;          // go forward to the next character
                
                bExpectNumber = TRUE;   // do expect the next character is a part of the number
                
                pointer_to_pointer_to_float++;         // move the pointer to the next receiving cell
                pointer_to_pointer_to_int++;           // move the pointer to the next receiving cell
            }
        }
        
        // Check for ordinar number in demical system (float or integer)
        if( !IsFloatChar( *str ) )
        {
            // hmmm, it does not relate to ordinary number...
            
            // check for the numeric system prefix!
            if( IsNumericSystem( str ) )
            {
                // success, prefix presents
                
                // retrieve the numeric system code
                nsys = GetNumericSystemBase( str );                
            }
            else
            {
                // prefix is absent                
                nsys = 0;
            }
        }
        else
        {
            nsys = 10;  // assume this number is in demical numeric system
        }
        
        // the character is not "white" and is not separator for sure.
        // the character is a part of the number for sure
        if( !bExpectNumber )
        {
            // but the number is unexpected now
            
            // error
            rval = -1;
            break;
        }
          
        count--;
        
        switch( nsys )
        {
            case 2:      // binary
            case 8:      // octal
            case 16:     // hexademical
            {
                uint32_t num;     // retrieved number from @ConvertNumerationSystem()
                
                // convert the number into demical system
                if( ConvertNumerationSystem( str, nsys, &num, &str ) )
                {
                    switch(type)
                    {
                        // the integer conversion is choosed
                        case USBTMC_PARSENUMBERS_TYPE_INT:
                                // store the number into the cell
                                (*pointer_to_pointer_to_int)[0] =   (int)num;
                        break;
                        
                        // the float conversion is choosed
                        case USBTMC_PARSENUMBERS_TYPE_FLOAT:
                        default:
                                // store the number into the cell
                                (*pointer_to_pointer_to_float)[0] = (float)num;
                        
                    }    
                        
                    bExpectNumber = FALSE;  // Since now the number is unexpected
                }
                else
                {
                    // error: can not convert the number
                    nsys = 0;   // error
                    rval = -1;  
                }
            }      
            break;
            
            case 10:
            {
                switch(type)
                {
                        // the integer conversion is choosed
                        case USBTMC_PARSENUMBERS_TYPE_INT:
                                // store the number into the cell
                                (*pointer_to_pointer_to_int)[0] =   atoi( str );
                        break;
                        
                        // the float conversion is choosed
                        case USBTMC_PARSENUMBERS_TYPE_FLOAT:
                        default:
                                // store the number into the cell
                                (*pointer_to_pointer_to_float)[0] = atoi( str );
                        
                } 
            }
            break;
            
            default: 
                rval = -1;  // unknown numeric system
                nsys = 0;   // error
        }
        
        // check if the numeric system detected
        // Also, @nsys=0 is a error signature to break from [while]
        if( 0 == nsys )
        {
            // error: invalid character
            rval = -1;
            break;
        }
    }   
    
    return rval;  // amount of unparsed numbers
}

// ConvertNumerationSystem:
// String to Number conversion.
// Input string format: <ns_prefix><ns_number>[, <ns_prefix><ns_number>...]
// where:
//  @ns_prefix - is a numeric system prefix:
//    "#H" or "#h"  - for the number in hexademical format;
//    "#B" or "#B"  - for the number in binary format;
//    "#O" or "#o"  - for the number in octal format;
//  @ns_number - the number in the one of the described above numeric systems;
// E.g, #B10001, #HDEADBEEF
// @str is a input string containing one or more numbers split up with the separator
// @nsys specifies the source numeric system. Can be zero to detect the system automatically.
// The return value is TRUE in case the number is parsed successfully and written into @pResult
// The return value is FALSE, in case the numeric system is not detected or unknown, or
//  if input string contains invalid characters for the number (but actually non-white-characters)
// The return value is FALSE if even the parsed number overflows the receiving cell (int)
// The function parses until the first "white" character is faced.
// In case of success (TRUE), the function modifies the @ppEnd in order to point to the last
// character parsed in @str.
#define ConvertNumerationSystem_limit_nsyshex   8
#define ConvertNumerationSystem_limit_nsysoct   11
#define ConvertNumerationSystem_limit_nsysbin   32
BOOL ConvertNumerationSystem( const char * str, int nsys, uint32_t * pResult, const char * * ppEnd )
{        
    if( 0 == nsys )
      nsys = GetNumericSystemBase( str );

    // skip the prefix
    str += GPIB_NUMBER_NSYSPREFIX_SIZE;
    
    size_t idx = 0;         // current position
    uint32_t result = 0;    // the result
    const char * iter;      // iterator for @str
    
    // search for the end of the number
    for( iter = str; !IsEndOfNumber( *iter ); ++iter, ++idx )
    {
        // check the character for if it relates to the assumed numeric system.
        switch( nsys )
        {
            //----------------------
            // Binary system
            case 2: 
                // the next bin character is coming
                if( idx > ConvertNumerationSystem_limit_nsysbin )
                {
                    // the limit exceeding (32 chars): the number can not be converted
                    return FALSE;
                }
                else
                    if( IsBinaryChar( *iter ) )
                    {
                        result <<= 1;
                        
                        result |= 0x1 & (BinCharToNum( *iter ));
                        
                        break; 
                    }
                    else
                        return FALSE;   // invalid character for binary numeric system
            //----------------------        
            // Octal system
            case 8:
                // the next oct character is coming
                if( idx > ConvertNumerationSystem_limit_nsysoct )
                {
                    // the limit exceeding (11 chars): the number can not be converted
                    return FALSE;
                }
                else
                    if( IsOctalChar( *iter ) )
                    {
                        if( ConvertNumerationSystem_limit_nsysoct == idx )
                        if( IsOCT_Int32Overflow( *iter ) )
                        {
                            // 32-bit integer overflow
                            return FALSE;
                        }
                        
                        result <<= 3;
                        
                        result |= 0x7 & (OctCharToNum( *iter ));
                            
                        break; 
                    }
                    else
                        return FALSE;   // invalid character for octal numeric system
            //----------------------
            // Hex system        
            case 16:
                // the next hex character is coming
                if( idx > ConvertNumerationSystem_limit_nsyshex )
                {
                    // the limit exceeding (8 chars): the number can not be converted
                    return FALSE;
                }
                else
                    if( IsHexademicalChar( *iter ) )
                    {
                        result <<= 4;
                        
                        result |= 0xF & (HexCharToNum( *iter ));
                        
                        break; 
                    }
                    else
                        return FALSE;   // invalid character for hexademical numeric system
            break;
        }
    } 
    
    // store the result
    if( NULL != pResult )
        *pResult = result;
    
    // store the iterator
    if( NULL != ppEnd)
        *ppEnd = iter;
            
            
    return TRUE;
}

size_t GPIB_Integer2Str( int32_t value, char * strBuf, size_t strBufSize, uint32_t compound_format )
{
    size_t nSym = 0;
    
    sFormatInt2Str_t * format = (sFormatInt2Str_t*)(&compound_format);
    
    if( format->nLeadDigits > 10 )  // 10 digits max for int32_t
      return 0; // error

    if( 0 != format->nLeadDigits )
    {
        char formatstr[8] = { '\0' };
        
        snprintf( formatstr, sizeof(formatstr), "%c%c%d%c", '%', '0', format->nLeadDigits, 'd' );
        
        nSym = snprintf( strBuf, strBufSize, formatstr, value );
    }
    else
    {  
        nSym = snprintf( strBuf, strBufSize, "%d", value );
    }

    if( nSym >= strBufSize )    
      return 0; // error       
    
    return nSym;
}

size_t GPIB_Float2Str( float value, char * strBuf, size_t strBufSize, uint32_t compound_format )
{
    return GPIB_Double2Str( (double)value, strBuf, strBufSize, compound_format );
}

size_t GPIB_Double2Str( double value, char * strBuf, size_t strBufSize, uint32_t compound_format )
{
    size_t nSym = 0;
    
    sFormatDouble2Str_t * format = (sFormatDouble2Str_t*)(&compound_format);
    
    if( 0 != format->bSciFormat )
    {   
        // science format
        {
            char formatStr[16] = {0};
            size_t n = format->nFracDigits;
            if( 0 == n ) n = 10;
            snprintf( formatStr, sizeof(formatStr) - 1, 
                      "%c%c%d%c", '%', '.', n, 'e' );
          
            int rc = snprintf( strBuf, strBufSize,
                             formatStr, value );
            if( rc < 0 )
                return 0;
                        
            if( rc >= strBufSize )
                return 0;
                        
            return (size_t)(rc);
        }
      
        /*
        bool sign = false;
        bool zero = false;
                
        if( value < 0 )
        {
             sign = true;
             value = -value;
        }
        
        // exponent part of science format,
        // e.g. for 0.0325: 0.0325 = 3.25e-2; exp = -2  
        // e.g. for -15.993: -15.993 = -1.5993e+1; exp = 1
        // e.g. for 2.775: 2.775 = 2.775e+0; exp = 0
        int exp = 0;  
        
        if( (value <= 1.0e-99) || (0.0 == value) )
        {
            exp = 0;
            (void)value;
            zero = true;
        }
        else
        if( (value <= 1) || (value >= 10) )
        {
            exp = (int)log10( value );
            value /= pow( 10, exp );
        }
        
        if( value < 1 && (value > 1.0e-99) )  // value less than 1.0e-99 is considered as zero
        {
            value *= (double)10.0;
            exp--;
        }
        else
        if( value >= 10 ) 
        {
            value /= (double)10.0;
            exp++;
        }
        
        // The function prints the value X.XXXXXXXXXXe+N
        // It is required to round the value up to 10 signs
        // 7.9999999999e+1 = 7.99999999990e+1
        // 7.99999999990e+1 + 0.00000000005 = 7.99999999995e+1
        // 7.99999999995e+1 => 8.0e+8
        //value += 5.0 * pow( 10, exp - 9);
        if( !zero )
        {  
            value += 5.0e-11;
        }
        
        size_t expChars = 1;  // 1 char for exp=0, "0"
        // expChars: amount of demical characters of @exp
        if( 0 != exp )
        {              
            expChars = (size_t)ceil( log10( abs( exp ) ) );
        }
        
        if( 0 == format->nFracDigits )
        {
            format->nFracDigits = 10;
        }
        
        // science format
        if(   format->nFracDigits // fractional part
            + 1  // integer part 
            + 1  // dot 
            + (sign?1:0) // sign
            + 2  // e+ or e-
            + expChars  
            > strBufSize )
        {
            // not enough free space in the buffer
            return 0;
        }
        
        // print a sign
        if( sign ) 
        {  
            if( strBufSize > 0 )
            {
                 strBuf[nSym] = '-';
                 nSym++;
                 strBufSize--;             
            }
            else
            {
                 nSym = 0;
                 goto L_Double2Str_EXIT;
            }
        }
        
        if( strBufSize > 0 )
        {
             int i = (int)value;
             
             strBuf[nSym] = '0' + i;
             strBufSize--;
             nSym++;
             //if( i > 9 ) asm("bkpt #0");
             value -= i;   // substract integer part
        }
        else
        {
             nSym = 0;
             goto L_Double2Str_EXIT;
        }
        
        if( strBufSize > 0 )
        {
            strBuf[nSym] = '.';
            nSym++;
            strBufSize--;
        }
        else
        {
             nSym = 0;
             goto L_Double2Str_EXIT;
        }
        
        if( strBufSize > 0 )
        {
            size_t fracDigits = format->nFracDigits;
            
            if( fracDigits == 0 )
                fracDigits  = 10;

            while( fracDigits > 0 )
            {          
                value *= 10;
                
                int digit = (int)(value);
                
                value -= digit;
                
                if( fracDigits > 0 )
                    fracDigits--;
                
                if( strBufSize > 0 )
                {
                    strBuf[nSym] = ('0' + digit);
                    nSym++;
                    strBufSize--;
                }
                else
                {
                    nSym = 0;
                    goto L_Double2Str_EXIT;
                }
            }
        }
        else
        {
             nSym = 0;
             goto L_Double2Str_EXIT;
        }
        
        if( 2 + expChars <= strBufSize )
        {
            strBuf[nSym] = 'e';
            nSym++;
            strBuf[nSym] = ((exp<0)?'-':'+'); 
            nSym++;
            exp = abs(exp);
            
            do
            {
                if( 0 == strBufSize )
                {
                    nSym = 0;
                    goto L_Double2Str_EXIT;
                }
                
                int digit = 0;
                int power = 0;
                
                if( exp != 0 )
                {  
                    power = (int)log10( exp );
                    power = (int)pow( 10, power );
                    
                    digit = (exp / power);    
                }
                    
                strBuf[nSym] = '0' + digit;
                nSym++;
                strBufSize--;

                exp -= power * digit;
            }
            while( exp > 0 );
                        
            
        }
        else
        {
             nSym = 0;
             goto L_Double2Str_EXIT;
        }
        */
    }
    else
    {   
        // float point format
        if( 0 == format->nFracDigits )
        {
            format->nFracDigits = 8;
        }
        
        {
            char formatStr[16] = {0};
            
            if( 0 == format->nLeadDigits )            
                snprintf( formatStr, sizeof(formatStr)-1,
                          "%c%c%d%c", '%', '.', format->nFracDigits, 'f' );
            else
                snprintf( formatStr, sizeof(formatStr)-1,
                          "%c%d%c%d%c", '%', format->nLeadDigits, '.', format->nFracDigits, 'f' );
            
            int rc = snprintf( strBuf, strBufSize,
                               formatStr, value );
            
            if( rc < 0 )
                return 0;
            
            if( rc >= strBufSize )
                return 0;
            
            return (size_t)rc;
        }
      
        /*
        if(   format->nFracDigits
            + format->nLeadDigits
            + 1  // dot 
            + (0>value?1:0) // sign 
            > strBufSize )
        {
            // not enough free space in the buffer
            return 0;
        }
        
        // print a sign
        if( value < 0 )
        {
             strBuf[nSym] = '-';
             nSym++;
             strBufSize--;
             value = -value;
        }
        
        // Process leading zeros
        {
            size_t leadDigits = 1;            
            // for numbers @value greater than 1 or equal the one,
            // the amount of leading digits can be caluculated as following.
            // Otherwise, for numbers less than 1, this amount is 1.
            // e.g. leadDigits =3 for 100.2
            //      leadDigits =2 for 97.8
            //      leadDigits =1 for 1.999
            //      leadDigits =1 for 1.000
            //      leadDigits =1 for 0.538
            if( value >= 1 )
            {
                // calculate amount of leading integer digits of the @value
                leadDigits = (int)ceil( log10( value ) );
            }
            else              
            {
                // The value is less than 1.
                // In this case a leading zero must be added.                
                if( format->nLeadDigits == 0 )
                {  
                    // If @nLeadDigits not specified,
                    // it is required to make the next condition true,
                    // to peform adding a leading zero
                  
                    //format->nLeadDigits = (1 + leadDigits);                    
                }
            }
                        
            // Specified number of lead digits in the format
            // greater than actual count of lead digits: need to
            // add leading zeros.
            if( (leadDigits < format->nLeadDigits)  )
            {
                //if( format->nLeadDigits == 0 )
                //  (void)leadDigits;             // do not change value
                //else 
                  leadDigits = format->nLeadDigits - leadDigits;
                
                // add leading zeros
                while( leadDigits > 0 )
                {
                    strBuf[nSym] = '0';
                    nSym++;
                    strBufSize--;
                    leadDigits--;
                }
            }
        }
        
        {
            int power = 1;
            
            if( value > 1e-99 )  // the value less than 1e-99 is considered as zero
            {  
                power = (int)pow( 10, (int)log10( value ) );
            }
            
            if( 0 == power )
            {
                strBuf[nSym] = ('0');
                nSym++;
                strBufSize--;
            }
            
            while( power > 0 )
            {      
               int digit = (int)value / power;
               
               value -= digit * power;
               
               power /= 10;
               
               if( strBufSize > 0 )
               {
                   strBuf[nSym] = ('0' + digit);
                   nSym++;
                   strBufSize--;
               }
               else
               {
                   nSym = 0;
                   goto L_Double2Str_EXIT;
               } 
            }
        }
        
        if( strBufSize > 0 )
        {
            strBuf[nSym] = '.';
            nSym++;
            strBufSize--;
        }
                
        {
            size_t fracDigits = format->nFracDigits;
                        
            while( fracDigits > 0 )
            {          
                value *= 10;
                
                int digit = (int)(value);
                
                value -= digit;
                
                if( fracDigits > 0 )
                    fracDigits--;
                
                if( strBufSize > 0 )
                {
                    strBuf[nSym] = ('0' + digit);
                    nSym++;
                    strBufSize--;
                }
                else
                {
                    nSym = 0;
                    goto L_Double2Str_EXIT;
                }
            }
        }
        */
    }
    
//L_Double2Str_EXIT:
    return nSym;
}
