/*
 * vtable.c
 *
 *  Created on: Jun 25, 2025
 *      Author: jim
 */

#include "main.h"
#include "system.h"
#include "sleep.h"
#include "sample.h"
#include "serial.h"
#include "config.h"
#include "i2c_fram.h"
#include "uart3.h"
#include "uartstdio.h"
#include "user_io.h"
#include "serial.h"
#include "board_util.h"
#include "apf.h"
#include "apf_util.h"
#include "apf_math.h"
#include "apf_init.h"
#include "apf_numbr.h"

#include "stdint.h"
#include "string.h"
#include "stdbool.h"
#include "vtable.h"

extern int waitACK(uint32_t timeout_ms);    // serial_cmd_fxn.c
extern struct rxStateMach rx0;              // defd in serial.h
extern uint32_t serRxFlushAfterCmdFlg;      // serial_cmd_fxn.c

char *typeNameStrs[] = {"Nope", "Int8", "I16", "I32", "U32", "Flt", "FltA", "Dbl", "Str" };

// For additive calls to varValueSet
char ackAccumulatorStr [64];

uint32_t tempUInt;

#if BOARD_MSC == 3
#define SC3V(...)   __VA_ARGS__
#else
#define SC3V(...)
#endif

// -----------------------------------------------------------------------------
//      Forward referencing for the specific verify functions
// -----------------------------------------------------------------------------
static int sensorModeVerify(void *p);
static int periodVerify(void *p);
static int filenameVerify(void *p);
static int spsVerify(void *p);

// -----------------------------------------------------------------------------

static char *varMinMaxStr(tVarEntry *varP)
{
    static char minMaxStrBuf[48];
    switch (varP->type)
    {
        case eFloat :
        case eFloatA :
            if (varP->min.Flt < -1999.0 || varP->max.Flt > 1999.0)
                sprintf(minMaxStrBuf, "[%.5e, %.5e]", varP->min.Flt, varP->max.Flt);
            else
                sprintf(minMaxStrBuf, "[%.2f, %.2f]", varP->min.Flt, varP->max.Flt);
            break;
        case eInt8 :
        case eInt16 :
        case eInt32 :
            sprintf(minMaxStrBuf, "[%d, %d]", varP->min.I32, varP->max.I32);
            break;
        case eUInt32 :
            sprintf(minMaxStrBuf, "[%u, %u]", varP->min.U32, varP->max.U32);
            break;
        case eString :
            sprintf(minMaxStrBuf, "[L %d]", varP->max.U32);
            break;
        default :
            sprintf(minMaxStrBuf, "*Unk-%d", varP->type);
            break;
    }
    return minMaxStrBuf;
}

static char *varValueStr(tVarEntry *varP)
{
    static char varValueStrBuf[48];
    float fVal;
    uint32_t u32;
    int32_t i32;
    int32_t i32End;
    int8_t i8;
    int16_t i16;
    int secondArg = FALSE;
    int validVerification = TRUE;

    // since the dP2 is a union type validate it outside the switch statement
    if (varP->flag & fVerifyFn )
    {
        validVerification = varP->dP2.func(varP->dP.v);

        uprintf("%sValid", (validVerification? "" : "In")); prn_nl();
    }
    else if (varP->dP2.i32)
        secondArg = TRUE;

    switch (varP->type)
    {
        case eFloat :
        case eFloatA :
            fVal = *(varP->dP.flt);
            if (fVal > 1999 || fVal < -1999)
                sprintf(varValueStrBuf, "%.5e", fVal);
            else
                sprintf(varValueStrBuf, "%.2f", fVal);
            break;
        case eInt8 :
            i8 = *(varP->dP.i8);
            sprintf(varValueStrBuf, "%d", i8);
            break;
        case eInt16 :
            i16= *(varP->dP.i16);
            sprintf(varValueStrBuf, "%d", i16);
            break;
        case eInt32 :
            i32 = *(varP->dP.i32);
            if (secondArg)
            {
                i32End = *(varP->dP2.i32);
                sprintf(varValueStrBuf, "%d, %d", i32, i32End);
            }
            else
            {
                sprintf(varValueStrBuf, "%d", i32);
            }
            break;
        case eUInt32 :
            u32 = *(varP->dP.u32);
            sprintf(varValueStrBuf, "%d", u32);
            break;
        case eString :
            sprintf(varValueStrBuf, "%s", varP->dP.str);
            break;
        default :
            sprintf(varValueStrBuf, "*Val-%d", varP->type);
            break;
    }
    return varValueStrBuf;
}

static void showArray(float *fptr, int arrayLen)
{
    int idx;
    for (idx=0; idx<arrayLen; idx++)
    {
        if (idx % 5 == 0)
        {
            prn_nl();
            uprintf("            ");
        }
        uprintf("[%d]=%.5f,  ", idx, *fptr++);
    }
}

typedef struct tFieldNameRec
{
    char        id;
    char        *name;
} tFieldNameRec;
tFieldNameRec fieldNames[] = { {'E',"Time" }, {'T',"Temp" }, {'S',"Salinity" }, {'P',"Depth" }, {0, "unk"} };

static char *fieldVarLookup(tVarEntry *varP)
{
    tFieldNameRec *fnr = fieldNames;
    while (fnr->id != 0)
    {
        if (fnr->id == varP->fieldVar)
            return fnr->name;
        fnr++;
    }
    // will return the "unk"
    return fnr->name;
}

/* xxxxx */
// The Entry table can handle pairs of numbers that have a start end and gap or no gap
// The numeric entries also have a min and max.
// the location in the structure is set after the ACK is completed.
// the CTD entry makes use of the field Var character and ACK is only required at the end.  In this case if the ACK is
// not returned the calling function must restore the original values.
// Verification
//              the argument is checked for Table Entries MIN and MAX
//              More complex checks can be down with a verify function.
//                var->DP2 points to a verify function that is passed the argument entered - returns TRUE if successful.
tVarEntry varTable[] =
{
     // NAME,         FLD,  TYPE,      MIN,                MAX,                          VAL1 Store,                            GAP/ARRAY MAX,  VAL2,                                  ACK/Store Flags
     { "aligned",       0, eInt8,       0,                  1,                          &sys_data.sample_aligned,                   0,          0,                                      fEEPStore },
     { "biasneg",       0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_BIAS_BAT_NEG],       0,          0,                                      fEEPStore },
     { "biaspos",       0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_BIAS_BAT_POS],       0,          0,                                      fEEPStore },
SC3V({ "cal_date_isus", 0, eString,     0,                  .max.I32=MAX_ISUS_CAL_DATE, apfArray.CalDate,                           0,          0,                                      fAArrayStore },)
     { "cal_date_pt",   0, eString,     0,                  .max.I32=MAX_PT_CAL_DATE,   sys_data.PT_Cal_date,                       0,          0,                                      fEEPStore },
     { "cal_date_k0",   0, eString,     0,                  .max.I32=MAX_K0_CAL_DATE,   sys_data.k0_Cal_date,                       0,          0,                                      fEEPStore },
SC3V({ "CTD",          'E',eUInt32,    .min.U32=MINRTC,    .max.U32=MAXRTC,            &apf.CTD_Time,                              0,          0,                                      fACKStart },)
SC3V({ "CTD",          'T',eFloat,     .min.Flt=MIN_TEMP,  .max.Flt=MAX_TEMP,          &apf.CTD_Temp,                              0,          0,                                      fACKAdd },)
SC3V({ "CTD",          'S',eFloat,     .min.Flt=MIN_SALT,  .max.Flt=MAX_SALT,          &apf.CTD_Salinity,                          0,          0,                                      fACKAdd },)
SC3V({ "CTD",          'P',eFloat,     .min.Flt=MIN_PRES,  .max.Flt=MAX_PRES,          &apf.CTD_Depth,                             0,          0,                                      fACKEnd+fAVarsStore },)
     { "curr_sample",   0, eInt32,     0,                  0,                          &sys_samp.current_sample,                   0,          0,                                      0 },
     { "duracalfile",   0, eString,    0,                  .max.I32=MAX_CAL_FILENAME,  sys_data.cal_filename,                      0,          0,                                      fEEPStore },

     // NAME,         FLD,  TYPE,      MIN,                MAX,                          VAL1 Store,                            GAP/ARRAY MAX,  VAL2,                                  ACK/Store Flags
     // For data that is entered in pairs and requires a GAP between low and high range the GAP specifier and second value data pointer is used.
SC3V({ "FIT",           0, eInt32,      .min.I32=215,       .max.I32=300,               &apf_eeprom.lambda_beg,                     10,         &apf_eeprom.lambda_end,                 fACKRequired },)
     { "filename",      0, eString,     3,                  12,                         sys_data.fileName,                          0,          .dP2.func=filenameVerify,               fVerifyFn+fEEPStore },

     { "ib_sps",        0, eInt32,      0,                 .max.I32=2000,               &sys_data.Ib_sps,                           0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "ib_trials",     0, eInt32,      0,                 80,                          &sys_data.Ib_trials,                        0,          0,                                      fEEPStore },
     { "ib",            0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_I_SUBSTRATE],        0,          0,                                      fEEPStore },

     { "ik_sps",        0, eInt32,      0,                 .max.I32=2000,               &sys_data.Ik_sps,                           0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "ik_trials",     0, eInt32,      0,                 80,                          &sys_data.Ik_trials,                        0,          0,                                      fEEPStore },
     { "ik",            0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_I_COUNTER],          0,          0,                                      fEEPStore },
     { "isfet_sn",      0, eString,     0,                  .max.I32=MAX_ISFET_SN,      sys_data.isfet_SN,                          0,          0,                                      fEEPStore },
     { "ise_sn",        0, eString,     0,                  .max.I32=MAX_ISE_SN,        sys_data.ise_SN,                            0,          0,                                      fEEPStore },

     { "k0_hcl",        0, eFloat,      .min.Flt=FLT_MIN,   .max.Flt=FLT_MAX,           &sys_data.k0_HCL,                           0,          0,                                      fEEPStore },
     { "k0_sw",         0, eFloat,      .min.Flt=FLT_MIN,   .max.Flt=FLT_MAX,           &sys_data.k0_SW,                            0,          0,                                      fEEPStore },
     { "k0_stdsw",      0, eFloat,      .min.Flt=FLT_MIN,   .max.Flt=FLT_MAX,           &sys_data.k0std_SW,                         0,          0,                                      fEEPStore },
     { "k2",            0, eFloatA,     .min.Flt=FLT_MIN,   .max.Flt=FLT_MAX,           &sys_data.K2_Cal,                           MAX_K2_IDX, 0,                                      fEEPStore },
     { "fp",            0, eFloatA,     .min.Flt=FLT_MIN,   .max.Flt=FLT_MAX,           &sys_data.fP_Cal,                           MAX_FP_IDX, 0,                                      fEEPStore },

SC3V({ "MODE",          0, eInt32,      0,                  3,                          &apf_fram.sys_sensor_mode,                  0,          .dP2.func=sensorModeVerify,             fVerifyFn+fACKRequired+fAVarsStore },)
SC3V({ "mscop",         0, eInt32,      0,                  1,                          &sys_data.msc_op,                           0,          0,                                      fEEPStore },)

     { "period",        0, eInt32,      0,                  .max.I32=3600,              &sys_data.sampling_period,                  0,          .dP2.func=periodVerify,                 fVerifyFn+fEEPStore },
SC3V({ "PROFILE",       0, eInt32,      0,                  .max.I32=9999,              &apf_fram.profileCnt,                       0,          0,                                      fACKRequired+fAVarsStore },)

     { "RTC",           0, eUInt32,     .min.U32=MINRTC,    .max.U32=MAXRTC,            &tempUInt,                                  0,          0,                                      fACKRequired },
     { "sampnum",       0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_SAMPLE_NUM],         0,          0,                                      fEEPStore },
     { "sensor_sn",     0, eString,     0,                  .max.I32=MAX_SENSOR_SN,     sys_data.sensor_SN,                         0,          0,                                      fEEPStore },
SC3V({ "sensor",        0, eUInt32,     1,                  4,                          &apf_eeprom.sys_sensor_type,                0,          0,                                      fAEEPStore }, )
SC3V({ "SPECTRA",       0, eInt32,      .min.I32=210,       .max.I32=350,               &apf_eeprom.lambda_dat_beg,                 10,         &apf_eeprom.lambda_dat_end,             fACKRequired },)

     // Some of the following appear to be switches that when set HIGH also set a second value - see the Store Flag - fSetIfHigh and uses the Val2 data pointer
     // NAME,         FLD,  TYPE,      MIN,                MAX,                          VAL1 Store,                            GAP/ARRAY MAX,  VAL2,                                  ACK/Store Flags
     { "tempc",         0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_CALC_TEMP],          0,          &sys_data.data_cfg[CFG_V_THERMISTOR],   fSetIfHigh+fEEPStore },
     { "timestamp",     0, eInt8,       0,                  2,                          &sys_data.data_cfg[CFG_TIMESTAMP],          0,          0,                                      fEEPStore },
SC3V({ "TINT",          0, eInt32,      .min.I32=MIN_TINT,  .max.I32=MAX_TINT,          &apf_eeprom.isus_spec_period,               0,          0,                                      fACKRequired },)
     { "vbat",          0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_VIN_BAT_VOLT],       0,          0,                                      fEEPStore },
     { "vb_sps",        0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vb_sps,                           0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vb_trials",     0, eInt32,      0,                 80,                          &sys_data.Vb_trials,                        0,          0,                                      fEEPStore },

     { "vbias_sps",     0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vbias_sps,                        0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vbias_trials",  0, eInt32,      0,                 80,                          &sys_data.Vbias_trials,                     0,          0,                                      fEEPStore },

     { "vk_std",        0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD],0,          0,                                      fEEPStore },
     { "vk_sps",        0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vk_sps,                           0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vk_trials",     0, eInt32,      0,                 80,                          &sys_data.Vk_trials,                        0,          0,                                      fEEPStore },
     { "vk",            0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_V_COUNTER_ELECT],    0,          0,                                      fEEPStore },

     { "vrse_std",      0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_VRSE_STD],           0,          &sys_data.data_cfg[CFG_VRSE],           fSetIfHigh+fEEPStore },
     { "vrse_sps",      0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vrse_sps,                         0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vrse_trials",   0, eInt32,      0,                 80,                          &sys_data.Vrse_trials,                      0,          0,                                      fEEPStore },
     { "vrse",          0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_VRSE],               0,          0,                                      fEEPStore },

     { "vrsi_std",      0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_VRSI_STD],           0,          &sys_data.data_cfg[CFG_VRSI],           fSetIfHigh+fEEPStore },
     { "vrsi_sps",      0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vrsi_sps,                         0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vrsi_trials",   0, eInt32,      0,                 80,                          &sys_data.Vrsi_trials,                      0,          0,                                      fEEPStore },
     { "vrsi",          0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_VRSI],               0,          0,                                      fEEPStore },

     { "vtherm_std",    0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_V_THERMISTOR_STD],   0,          &sys_data.data_cfg[CFG_V_THERMISTOR],   fSetIfHigh+fEEPStore },
     { "vtherm_sps",    0, eInt32,      0,               .max.I32=2000,                 &sys_data.Vtherm_sps,                       0,          .dP2.func=spsVerify,                    fVerifyFn+fEEPStore },
     { "vtherm_trials", 0, eInt32,      0,                 80,                          &sys_data.Vtherm_trials,                    0,          0,                                      fEEPStore },
     { "vtherm",        0, eInt8,       0,                  1,                          &sys_data.data_cfg[CFG_V_THERMISTOR],       0,          0,                                      fEEPStore },

     { "offset_ik",     0, eInt32,      0,                 80,                          &sys_data.offset_Ik,                        0,          0,                                      fEEPStore },
     { "offset_ib",     0, eInt32,      0,                 80,                          &sys_data.offset_Ib,                        0,          0,                                      fEEPStore },
     { "",              0, eNone,       0,                  0,                          NULL,                                       0,          0,                                      0 }
};



void cmd_show_vars(void)
{
    tVarEntry *varP = varTable;
    wdog_pet();

    while (varP->type != eNone)
    {
        if (varP->type == eFloatA)
        {
            float *fptr = varP->dP.flt;
            uprintf("%12s  %5s Range:%s", varP->symbol, typeNameStrs[varP->type], varMinMaxStr(varP));
            showArray(fptr, varP->gap);
        }
        else if (varP->type == eString)
        {
            uprintf("%12s=%-8s %5s MaxLen:%d", varP->symbol, varValueStr(varP), typeNameStrs[varP->type], varP->max.U32);
        }
        else
        {
            if (varP->fieldVar)
            {
                uprintf("%12s-%s=%-8s %5s Range:%s", varP->symbol, fieldVarLookup(varP), varValueStr(varP), typeNameStrs[varP->type], varMinMaxStr(varP));
            }
            else
                uprintf("%12s=%-8s %5s Range:%s", varP->symbol, varValueStr(varP), typeNameStrs[varP->type], varMinMaxStr(varP));
        }
        prn_nl();
        varP++;
        wait_consoleTx();
    }
}

tVarEntry *findVar(char *lookup)
{
    tVarEntry *varP = varTable;
    wdog_pet();

    while (varP->type != eNone)
    {
        if (strcmp(varP->symbol, lookup) == 0)
        {
            wdog_pet();
            return varP;
        }
        varP++;
    }

    // uprintf("Symbol:%s Not found!", lookup);
    // prn_nl();
    return NULL;
}

tVarEntry *findVarField(char *lookup, char fieldLookup)
{
    tVarEntry *varP = varTable;
    wdog_pet();

    while (varP->type != eNone)
    {
        if ((strcmp(varP->symbol, lookup) == 0) && (varP->fieldVar == fieldLookup))
        {
            wdog_pet();
            return varP;
        }

        varP++;
    }

    uprintf("Symbol:%s [%c] Not found!", lookup, fieldLookup);
    prn_nl();
    return NULL;
}

int setDataVar(void)
{
    char *strptr = (char *)&rx0.buf[0];
    char *varName = strtok(strptr, ", =[");
    tVarEntry *varP = findVar(varName);

    if (varP)
    {
        // Maybe we need some additional rules??
        switch(varP->type)
        {
            case eFloatA :
                strptr = strtok(NULL, "]");         // second token gives null terminated first parm
                break;

            default :                               // JLD!!! removed the "." here
                strptr = strtok(NULL, " ");         // second token gives null terminated first parm
                break;
        }

        if (strptr)
        {
            if (varValueSet(varP, strptr) == OK)
            {
                return OK;
            }
            return ERROR;       // Found and tried to set a value - no more needed
        }
        else
        {
            uprintf("%s=%s", varP->symbol, varValueStr(varP));
            prn_nl();
            return OK;
        }
    }
    return -7;      // Not found
}

// qconfig=N   N is sensor config returned by Q command:  1 = No Sensor, 2 = ISUS Nitrate, 3 = ISFET pH, 4 = Nitrate + pH")
extern void cmd_set_var(void)
{
    tVarEntry *varP = NULL;
    char *strptr = (char *)&rx0.buf[0];
    strptr = strtok(strptr, ", =");
    char *varName = strtok(NULL, ", =[");

    if (varName)
    {
        varP = findVar(varName);
        if (varP)
        {
            // Maybe we need some additional rules??
            switch(varP->type)
            {
                case eFloatA :
                    strptr = strtok(NULL, "]");            // second token gives null terminated first parm
                    break;
                default :
                    strptr = strtok(NULL, " ");            // second token gives null terminated first parm
                    break;
            }
            if (varValueSet(varP, strptr) == OK)
            {
                // previously this is where it was stored.
                // uprintf("confirmed modified %s", varP->symbol); prn_nl();
            }
            return;
        }
        uprintf("Var %s not found!", varName);
        prn_nl();
        return;
    }
    uprintf("Var not found!");
    prn_nl();
}

void showVar(tVarEntry *varP)
{
    if (varP)
    {
        if (varP->type == eFloatA)
        {
            float *fptr = varP->dP.flt;
            char *strptr = strtok(NULL, "]");
            if (strptr)
            {
                int arrayIndx = strtol(strptr, 0, 10);
                if (arrayIndx < varP->gap)
                {
                    if (fptr[arrayIndx] > 1999.9)
                        uprintf("%s[%d]=%.3e", varP->symbol, arrayIndx, fptr[arrayIndx]);
                    else
                        uprintf("%s[%d]=%.5f", varP->symbol, arrayIndx, fptr[arrayIndx]);
                }
                else
                {
                    uprintf("Index %d is out of bounds for %s [0-%d]!", arrayIndx, varP->symbol, varP->gap);
                }
            }
            else
            {
                showArray(fptr, varP->gap);
            }
        }
        else
        {
            uprintf("%s=%s", varP->symbol, varValueStr(varP));
        }
    }
}

extern void cmd_get_var(void)
{
    char *strptr = (char *)&rx0.buf[0];
    strptr = strtok(strptr, ", =");
    char *varName = strtok(NULL, ", =[");
    tVarEntry *varP = NULL;
    if (varName)
    {
        varP = findVar(varName);
        showVar(varP);
    }
    else
    {
        uprintf("Var %s not found!", varName);
    }

    prn_nl();
}


int varValueSet(tVarEntry *var, char *strptr)
{
    int32_t t32;
    int32_t t32End;
    uint32_t u32;
    float   fVal;
    float   fValEnd;
    char    *strptr2;
    char    *endptr = "";
    char    *endptr2 = "";
    char    strBuf[MAX_STRING_VAR_LEN];
    int     result;
    int     cnt = 1;
    int     err1 = 0;
    int     err2 = 0;
    int     arrayIndx;

    if (strptr && *strptr)
    {
        //uprintf("%s Start - strptr %s", __FUNCTION__, strptr); prn_nl();
        //wait_consoleTx();

        switch (var->type)
        {
            case eInt8 :
            case eInt16 :
            case eInt32 :
            {
                errno = 0;
                t32  = strtol(strptr, &endptr, 10);
                err1 = errno;
                if (var->gap)
                {
                    cnt++;
                    strptr2 = strtok(NULL, ",");
                    if (*strptr2)
                    {
                        errno = 0;
                        t32End  = strtol(strptr2, &endptr2, 10);
                        err2 = errno;
                    }
                    else
                    {
                        uprintf("NAK,%s,FormatErr", var->symbol); prn_nl();
                        return -1;
                    }
                }
            }
            break;

            case eUInt32 :
                errno = 0;
                u32  = strtoul(strptr, &endptr, 10);
                //uprintf(" U32 computed %u", u32); prn_nl();

                err1 = errno;
                if (var->gap)
                {
                    cnt++;
                    strptr2 = strtok(NULL, ",");
                    if (*strptr2)
                    {
                        errno = 0;
                        t32End  = strtoul(strptr2, &endptr2, 10);
                        err2 = errno;
                    }
                    else
                    {
                        uprintf("NAK,%s,%cFormatErr", var->symbol, var->fieldVar); prn_nl();
                        return -1;
                    }
                }
                // JLD - RTC checked for less than zero and had this error message possible:
//                if (rtcValue < 0)  { uprintf("NAK,RTC,SignErr: %d", rtcValue);  prn_nl(); }
                break;

            case eFloat :
                errno = 0;
                fVal = strtof(strptr, &endptr);
                //uprintf(" FLOAT computed %f", fVal); prn_nl();
                //wait_consoleTx();

                err1 = errno;
                if (var->gap)
                {
                    cnt++;
                    strptr2 = strtok(NULL, ",");
                    if (*strptr2)
                    {
                        errno = 0;
                        fValEnd  = strtof(strptr2, &endptr2);
                        err2 = errno;
                    }
                    else
                    {
                        uprintf("NAK,%s,%cFormatErr", var->symbol, var->fieldVar); prn_nl();
                        return -1;
                    }
                }
                break;

            case eFloatA :
            {
                arrayIndx = strtol(strptr, &endptr, 10);
                if (arrayIndx < var->gap)
                {
                    strptr2 = strtok(NULL, "=");
                    errno = 0;
                    fVal = strtof(strptr2, &endptr);
                }
                else
                {
                    uprintf("Array index out of bounds [0-%d] %d is invalid!", var->gap-1, arrayIndx);
                    prn_nl();
                    return -1;
                }
            }
            break;

            case eString :
                strcpy(strBuf, strptr);
                strptr2 = strtok(NULL, ".");
                // uprintf("string got [%s] and [%s]", strptr, strptr2);
                // prn_nl();
                break;

            default : { uprintf("NYI %d!", var->type); prn_nl(); break; }
        }
    }
    else
    {
        uprintf("NAK,%s,FormatErr", var->symbol); prn_nl();
        return -1;
    }

    //uprintf(" err1 = %d err2 = %d", err1, err2); prn_nl();
    //wait_consoleTx();

    if (err1 == ERANGE)
    {
        if (cnt == 2)
        {
            uprintf("NAK,%s,SOverflowErr: %s", var->symbol, strptr); prn_nl();
        }
        else
        {
            uprintf("NAK,%s,%cOverflowErr: %s", var->symbol, var->fieldVar, strptr); prn_nl();
        }
        return -2;
    }

    if (err2 == ERANGE)
    {
        uprintf("NAK,%s,EOverflowErr: %s", var->symbol, strptr); prn_nl();
        return -2;
    }

    if ((err1 == EINVAL) || (*endptr))
    {
        //uprintf("err1 or endptr %s", err1, endptr); prn_nl();

        if (cnt == 2)
            uprintf("NAK,%s,SConvertErr: %s", var->symbol, strptr);
        else if (var->type == eFloatA)
            uprintf("NAK,%s,ConvertErr: %s", var->symbol, strptr2);
        else
            uprintf("NAK,%s,%cConvertErr: %s", var->symbol, var->fieldVar, strptr);
        prn_nl();
        return -3;
    }
    if ((err2 == EINVAL) || (*endptr2))
    {
        uprintf("NAK,%s,EConvertErr: %s", var->symbol, strptr2); prn_nl();
        return -3;
    }

    // Do range checking and gap checking if two arguments are present
    switch (var->type)
    {
        case eFloat :
        case eFloatA :
            if (cnt == 1)
            {
                //uprintf(" FLOAT range check %f", fVal); prn_nl();
                //uprintf(" min %f  max %f", var->min.Flt, var->max.Flt); prn_nl();
                //wait_consoleTx();

                if ((fVal < var->min.Flt) || (fVal > var->max.Flt))
                {
                    uprintf("NAK,%s,%cRangeErr [%.1f,%.1f]: %s", var->symbol, var->fieldVar, var->min.Flt, var->max.Flt, strptr);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -7;
                }
            }
            else if (cnt == 2)
            {
                if ((fVal < (float)var->min.Flt) || (fVal > (float)var->max.Flt))
                {
                    uprintf("NAK,%s,SRangeErr [%.1f,%.1f]: %s", var->symbol, var->min.Flt, var->max.Flt, strptr);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -7;
                }
                if ((fValEnd < (float)var->min.Flt) || (fValEnd > (float)var->max.Flt))
                {
                    uprintf("NAK,%s,ERangeErr [%.1f,%.1f]: %s", var->symbol, var->min.Flt, var->max.Flt, strptr2);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -4;
                }
                if( fValEnd <= fVal )
                {
                    uprintf("NAK,%s,EVal<SVal: %.1f, %.1f", var->symbol, fVal, fValEnd); prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -5;
                }
                if( (fValEnd - fVal) <= (float)var->gap )
                {
                    uprintf("NAK,%s,EVal-SVal!>%d: %.1f, %.1f", var->symbol, var->gap, fVal, fValEnd); prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -6;
                }
            }
            break;

        case eInt8 :
        case eInt16 :
        case eInt32 :
        {
            if (cnt == 1)
            {
                //uprintf(" INT32 range check %d", t32); prn_nl();
                //uprintf(" min %d  max %d", var->min.I32, var->max.I32); prn_nl();

                if ((t32 < var->min.I32) || (t32 > var->max.I32))
                {
                    uprintf("NAK,%s,%cRangeErr [%d,%d]: %s", var->symbol, var->fieldVar, var->min.I32, var->max.I32, strptr);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -7;
                }
            }
            else if (cnt == 2)
            {
                if ((t32 < var->min.I32) || (t32 > var->max.I32))
                {
                    uprintf("NAK,%s,SRangeErr [%d,%d]: %s", var->symbol, var->min.I32, var->max.I32, strptr);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -7;
                }
                if ((t32End < var->min.I32) || (t32End > var->max.I32))
                {
                    uprintf("NAK,%s,ERangeErr [%d,%d]: %s", var->symbol, var->min.I32, var->max.I32, strptr2);  prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -4;
                }
                if( t32End <= t32 )
                {
                    uprintf("NAK,%s,EVal<SVal: %u, %u", var->symbol, t32, t32End); prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -5;
                }
                if( (t32End - t32) <= var->gap )
                {
                    uprintf("NAK,%s,EVal-SVal!>%d: %u, %u", var->symbol, var->gap, t32, t32End); prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    return -6;
                }
            }
        }
        break;
        case eUInt32 :
        {
            // TODO: JLD only handles the case for 1 argument, model for 2 arguments in the signed case above
            //uprintf(" UINT32 range check %u", u32); prn_nl();
            //uprintf(" min %u  max %u", var->min.U32, var->max.U32); prn_nl();

            if ((u32 < var->min.U32) || (u32 > var->max.U32))
            {
                uprintf("NAK,%s,%cRangeErr [%u,%u]: %s", var->symbol, var->fieldVar, var->min.U32, var->max.U32, strptr);  prn_nl();
                if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                return -7;
            }
        }
        break;
        case eString :
        {
            int len = strlen(strBuf);
            if (len > var->max.U32)
            {
                uprintf("NAK,%s,String exceeds max length [%d > %d]: %s", var->symbol, len, var->max.U32, strptr);  prn_nl();
                if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                return -7;
            }
        }
        break;

        default :
            uprintf("Range check not implemented for type %d", var->type); prn_nl();
            break;
    }

    // if we need to verify more complex settings...
    if (var->flag & fVerifyFn)
    {
        void *vdp;
        switch (var->type)
        {
            case eInt8 :
            case eInt16 :
            case eInt32 :
                vdp = &t32;
                break;
            case eUInt32 :
                vdp = &u32;
                break;
            case eFloat :
            case eFloatA :
                vdp = &fVal;
                break;
            case eString :
                vdp = strBuf;
                break;
            case eDouble :
                vdp = &fVal;
            default :
                break;
        }
        if (var->dP2.func(vdp) != 1)  // passing the void pointer here. The verify function is required to check the appropriate pointer.
        {
            uprintf("VerifyFn failed!");
            prn_nl();
            return -9;
        }
    }

    if (var->flag & fACKRequired)
    {
        if (cnt == 2)
        {
            switch (var->type)
            {
                case eInt32 : uprintf("ACK,%s,%d,%d", var->symbol, t32, t32End); break;
                case eUInt32 : uprintf("ACK,%s,%u,%u", var->symbol, t32, t32End); break;
                case eFloat  : uprintf("ACK,%s,%.1f,%.1f", var->symbol, fVal, fValEnd); break;
            }
        }
        else
        {
            switch (var->type)
            {
                case eInt32  : uprintf("ACK,%s,%d", var->symbol, t32); break;
                case eUInt32 : uprintf("ACK,%s,%u", var->symbol, u32); break;
                case eFloat  : uprintf("ACK,%s,%.1f", var->symbol, fVal); break;
                default : uprintf("ACK,%s,UNK", var->symbol); break;
            }
        }
        prn_nl();
    }
    else if (var->flag & fACKStart)
    {
        //uprintf(" ACK Start"); prn_nl();
        //wait_consoleTx();

        switch (var->type)
        {
            case eInt32  : sprintf(ackAccumulatorStr, "ACK,%s,%d", var->symbol, t32); break;
            case eUInt32 : sprintf(ackAccumulatorStr, "ACK,%s,%u", var->symbol, u32); break;
            case eFloat  : sprintf(ackAccumulatorStr, "ACK,%s,%.1f", var->symbol, fVal); break;
            default : uprintf(ackAccumulatorStr, "ACK,%s,UNK-%d", var->symbol, var->type); break;
        }

        //uprintf("     -->  [%s]", ackAccumulatorStr); prn_nl();
        //wait_consoleTx();

    }
    else if (var->flag & (fACKAdd | fACKEnd))
    {
        char *nextAccumPos = &ackAccumulatorStr[strlen(ackAccumulatorStr)];

        //uprintf(" --> ACK Add or End %X %X", nextAccumPos, ackAccumulatorStr); prn_nl();
        //wait_consoleTx();

        switch (var->type)
        {
            case eInt32  : sprintf(nextAccumPos, ",%d", t32); break;
            case eUInt32 : sprintf(nextAccumPos, ",%u",  u32); break;
            case eFloat  : sprintf(nextAccumPos, ",%.1f", fVal); break;
            default : uprintf(nextAccumPos, ",UNK-%d", var->type); break;
        }
        if (var->flag & fACKEnd)
        {
            uprintf("%s", ackAccumulatorStr);
            prn_nl();
        }
    }
    else
    {
        switch (var->type)
        {
            case eInt8 :
            case eInt16 :
            case eInt32  : uprintf("%s=%d", var->symbol, t32); break;
            case eUInt32 : uprintf("%s=%u", var->symbol, u32); break;
            case eFloat : uprintf("%s=%.5f", var->symbol, fVal); break;
            case eFloatA : uprintf("%s[%d]=%.5f", var->symbol, arrayIndx, fVal); break;
            case eString : uprintf("%s=%s", var->symbol, strBuf); break;
            default : uprintf("ACK,%s,UNK", var->symbol); break;
        }
    }

    if (var->flag & (fACKRequired | fACKEnd))
    {
        result = waitACK(GET_ACK_TIMEOUT_MSEC);
    }
    else
    {
        result = 2;
    }

    if (result)
    {
        #if BOARD_MSC == 3
        // make sure the array is read first
        if (var->flag & fAArrayStore)
            fram_apfArray_refresh();
        #endif

        switch (var->type)
        {
            case eInt8 :
                *(var->dP.i8) = (char)t32;
                // This extension is for switched variables.
                // Some are single pole double throw, but they only toggle one other variable if high - JLD!!! - TGM??? why?
                if ((var->dP2.i8 != 0) && (var->flag & fSetIfHigh))
                {
                    if (t32 == 1)
                        *(var->dP.i8) = (char)t32;
                }
                break;
            case eInt16 :
                *(var->dP.i16) = (short)t32;
                break;
            case eInt32 :
                *(var->dP.i32) = t32;
                if (cnt == 2)
                    *(var->dP2.i32) = t32End;
                break;
            case eUInt32 :
                *(var->dP.u32) = u32;
                break;
            case eFloat :
                *(var->dP.flt) = fVal;
                break;
            case eFloatA :
                {
                    float *fptr = var->dP.flt;
                    fptr [arrayIndx] = fVal;
                }
                break;
            case eString :
                strcpy(var->dP.str, strBuf);
                break;
            case eDouble :
            default :  uprintf("NYI %d!", var->type); prn_nl();
                break;
        }

        // Determine where the changed var gets stored
        if (var->flag & fEEPStore)
            eeprom_sys_data_store();
        #if BOARD_MSC == 3
        if (var->flag & fAEEPStore)
            eeprom_apf_cfg_store();
        if (var->flag & fAArrayStore)
            fram_apfArray_store();
        if (var->flag & fAVarsStore)
            fram_apf_vars_store();
        #endif

        return 0;
    }
    else
    {
        char ebuf[64];
        uprintf("NAK,%s,ACK Failure", var->symbol); prn_nl();
        sprintf(ebuf, "%s Command ACK Failure", var->symbol);
        LOGERRORMSG(ebuf, (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        return -8;
    }
}

// -----------------------------------------------------------------------------
//      Specific variables that require Verify functions go here:
// -----------------------------------------------------------------------------

#if BOARD_MSC == 3
static int sensorModeVerify(void *dp)
{
    // JLD-TGM - Im not sure why theres a bit of logic around setting this to 3 only if barf is on.  I dont understand.
    // So for now just validate and use the table min max values.
    return 1;
}
#endif

static int spsVerify(void *dp)
{
    // JLD-TGM pulled this from the config test
    int temp = *((int32_t *)(dp));

    if(temp == 5 || temp == 10 || temp == 20 || temp == 40 || temp == 80 || temp == 160
                 || temp == 320 || temp == 640 || temp == 1000 || temp == 2000)
    {
        return 1;
    }
    else
    {
        prn_nl(); uprintf("Enter 5, 10, 20, 40, 80, 160, 320, 640, 1000, or 2000");
        prn_nl();
    }
    return 0;
}

static int periodVerify(void *dp)
{
    int tempInt = *((int32_t *)(dp));
    if(sys_data.sample_aligned)     // Sample aligned to hour?
    {
        if((3600 % tempInt) && (tempInt > 0))     // BUG Fix 6 May 2019, per request to allow sample aligned to hour in polled mode (sample period = 0) - Not sure the sense of it though since in polled mode, the poller is determining the time
        {
            prn_nl();
            // JLD-TGM this example does not match the conditional check... 3600 % 2,3,4,6,8,9,10,12,16,18,20 all work as well
            // did you mean something else?
            // (e.g. 5, 10, 15, 20, 30 minutes)
            uprintf("sample period %d is not aligned with hour [%d] - must evenly divide into hour ", tempInt, 3600/tempInt);
            prn_nl();
            return 0;
        }
    }
    return 1;
}

// verify 8.3 format and a-z names - JLD-TGM --- checks for only alpha - no numbers!"
static int filenameVerify(void *p)
{
    char *fNamePtr = (char *)p;
    int fNameLen = strlen(fNamePtr);
    int idx = 0;

    // string length will have already been checked by the vTable main parser.
    while (idx < fNameLen)
    {
        // at the filename extension
        if (*fNamePtr == '.')
        {
            *(fNamePtr+4) = 0;        // simple, but does not check the alpha-numericness
            return 1;               // inserts a NULL terminator in the origin!!
        }

        if ((*fNamePtr < 'a') || (*fNamePtr > 'z'))
            return 0;

        // Advance the string pointer and counter
        fNamePtr++;
        idx++;
    }
    return 1;
}
