#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "app/scpi/scpi_core.h"
#include "app/scpi/scpi_core_private.h"
#include "app/scpi/scpi_commands.h"
#include "app/scpi/scpi_parser.h"
#include "app/scpi/scpi_numeric.h"
//----------------------------------------------------------------
// Refer to:
// [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
//      Standard Digital Interface for Programmable Instrumentation"
// [2] SCPI Specification, revision 1999.0
//     "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
//----------------------------------------------------------------


// @processNumericArgument
 // Numeric argument validation and conversion function.
 // Processes specified SCPI Numeric Parameter (Numeric Program Data) with the most length SCPI_MAX_NUMBER_LENGTH.
 // References:
 //     "7.7.2 <DECIMAL NUMERIC PROGRAM DATA>", [1]
 //     "7.7.2.2 Encoding Syntax", [1]
 //     "7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA>", [1]
 //     "7.7.4.2 Encoding Syntax", [1]
 // Parameters:
 //  @pObj - numeric conversion context;
 //  @argToken - numeric argument token to process;
 //  @entityType - SCPI entity type [eScpiEntityType_t] (see @getParsedEntityDetails, 'peEntityType' argument)
 //  @range - allowed values range structure, optional
 // Return:
 //  - true: parameter processed successfully, the result is stored inside numeric conversion context
 //  - false: paramenter processing failed.
 bool processNumericArgument( sProcessNumericEntry_t * pObj, const sStrToken_t * argToken, uint8_t entityType, const sNumericRange_t * range )
 {    
    bool status = false;

    assert( pObj );
    assert( argToken );
    
    // keep at least one free character left in the end of the buffer for null-character
    if( ((ptrdiff_t)argToken->stail - (ptrdiff_t)argToken->shead) >= SCPI_MAX_NUMBER_LENGTH )
    {
        pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE; // too long number
        return false;
    }

    pObj->valueEntry.error = ScpiNumericError;

    // fill buffer with zeros, the last character must be '\0'
    memset( pObj->numberBuffer, 0, sizeof(pObj->numberBuffer) );

    // copy number to the null-terminated buffer
    strncpy( pObj->numberBuffer,  argToken->shead, ((ptrdiff_t)argToken->stail - (ptrdiff_t)argToken->shead) ); 

    // fill the @valueType to specify the value type to be accessed in @pObj->Value 
    pObj->valueEntry.valueType = entityType;
    
    int base = 0; // forward set to invalid
    char * pEnd = NULL;

    switch( entityType )
    {
       // --- Float ----
       case eScpiEntityTypeDemicalFloatNumber:   // Demical Number (float, only mantissa)
       case eScpiEntityTypeDemicalExpNumber:     // Demical Number (scientific, mantissa+exponent)
       {
            // check range type
            if( ! SCPI_NUMERIC_LIMIT_TYPE_IS_FLOAT(range->type) )
            {
                // invalid range: user limit is set in integer limits
                // user expects that the value is integer, but it is not
                pObj->valueEntry.error = ScpiNumericError_USER_TYPE;
                (void)status; // false
            }
            else
            {
                pObj->valueEntry.Value.demicalFloat = strtold( pObj->numberBuffer, &pEnd );

                // check for conversion error
                if( errno != 0 /*ERANGE*/ )
                {
                    // invalid float conversion: ERANGE?
                    pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE;
                    (void)status; // false
                }
                // check for limits
                else if( NULL != range ) // user limit is set
                {
                    status = ( range->min.f <= pObj->valueEntry.Value.demicalFloat &&
                               range->max.f >= pObj->valueEntry.Value.demicalFloat );

                    if( !status )
                    pObj->valueEntry.error = ScpiNumericError_USER_RANGE;
                    else
                    pObj->valueEntry.error = ScpiNumericSuccess;
                }
                else
                {
                    status = true;
                    pObj->valueEntry.error = ScpiNumericSuccess;
                }
            }
       }
       break;
       
       // --- Integer ---
       case eScpiEntityTypeHexNumber:            // Hexademical Number
            if( 0 == base ) base = 16;
       case eScpiEntityTypeOctNumber:            // Octal Number
            if( 0 == base ) base = 8;
       case eScpiEntityTypeBinNumber:            // Binary Number
            if( 0 == base ) base = 2;
       case eScpiEntityTypeDemicalIntegerNumber: // Integer number only, extension for Demical Number
            if( 0 == base ) base = 10;
       {
            pObj->valueEntry.Value.demicalInteger = strtoll( pObj->numberBuffer, &pEnd, base );
            
            // check for conversion error
            if( errno != 0 /*ERANGE*/ )
            {
                // invalid float conversion: ERANGE?
                pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE;
                (void)status; // false
            }
            // check for limits
            else if( NULL != range )  // user limit is set
            {
                // check if the float range is set, but actually integer received
                if( SCPI_NUMERIC_LIMIT_TYPE_IS_FLOAT(range->type) )
                {
                    // yep: change processed value type to float (integer promotion to float)
                    // convert value
                    pObj->valueEntry.Value.demicalFloat = pObj->valueEntry.Value.demicalInteger;  // {type cast}
                    // change type
                    pObj->valueEntry.valueType = eScpiEntityTypeDemicalExpNumber;

                    // check limits
                    status = ( range->min.f <= pObj->valueEntry.Value.demicalFloat &&
                               range->max.f >= pObj->valueEntry.Value.demicalFloat );

                    if( !status )
                    pObj->valueEntry.error = ScpiNumericError_USER_RANGE;
                    else
                    pObj->valueEntry.error = ScpiNumericSuccess;
                }
                else
                if( SCPI_NUMERIC_LIMIT_TYPE_IS_INTEGER(range->type) )
                {
                    // no: integer limit for integer value
                    status = ( range->min.i <= pObj->valueEntry.Value.demicalInteger &&
                               range->max.i >= pObj->valueEntry.Value.demicalInteger );

                    if( !status )
                    pObj->valueEntry.error = ScpiNumericError_USER_RANGE;
                    else
                    pObj->valueEntry.error = ScpiNumericSuccess;
                }
                else
                {
                    (void)status; (void)pObj->valueEntry.error; // false, ScpiNumericError
                } 
            }
            else
            {
                status = true;
                pObj->valueEntry.error = ScpiNumericSuccess;
            }
       }
       break;
       default: (void)status; (void)pObj->valueEntry.error; // false, ScpiNumericError;
    }

    if( status )
    pObj->valueEntry.error = ScpiNumericSuccess;

    return status;
 }