/*
 * thomzone.c
 *
 *  Created on: Jul 30, 2024
 *      Author: tm
 */
#include "system.h"
#include "apf.h"
#include "apf_util.h"
#include "config.h"
#include "sample.h"
#include "user_io.h"
#include "microsd.h"
#include "i2c_fram.h"
#include "fatfs/source/ff.h"
#include "adc_onchip.h"
#include "ads1248_iso.h"
#include "ads1248_noniso.h"
#include "sleep.h"
#include "optode.h"
#include "ocr.h"
#include "picoph.h"
#include "nanofet.h"
#include "ctd_base.h"
#include "uartstdio.h"
#include "board_util.h"
#include "config.h"
#include "ctd_aand5860.h"
#include "ctd_sbe37.h"
#include "i2c_fram.h"
#include "i2c_ina260.h"
#include "i2c_ds3231.h"
#include "i2c0.h"
#include "user_io.h"
#include "test.h"
#include "i2c_bme280.h"
#include "i2c_pcal64xx.h"
#include "mms_spec.h"
#include "mms_test.h"
#include "spi_flash.h"
#include "serial_cmd_fxn.h"
#include "sample.h"
#include "main.h"

#include "crc/crc-8.h"

#include <time.h>


    // Test 0:
    //   - write a really big file, until something breaks

    // Test 1:
    //   - many files in a directory, until something breaks

    // Test 2:
    //   - many directories, many files

    // Test 3:
    //   - long file names
    //   - long directory names

    // Test 3:
    //   - make file, delete file
void microsd_fatfs_hammer_test(unsigned char testNum)
{

    uint32_t    indx;
    uint32_t    jndx;

    char timestamp[TIMESTAMPLENGTH];

    uint32_t tickStart, ticks, tickDelta;
    struct tm *time_struct;

    FIL         fTest;
    FRESULT     fresult;
    UINT        bw;
    FILINFO fno;

    uint32_t    fileCnt;
    char        fileName[256];

    float fSysVolt, fSysAmp, fLampVolt, fLampAmp; //, fLampStartVolt, fLampStartAmp;
    float       fBrdPressure, fBrdTempC;
    uint32_t    uiHumidity;
    int         inKey;
    uint32_t    onoff;


    // Test 0:
    //   - write a really big file, until something breaks

    led_green_on();

    wdog_pet();

    if(testNum == 0)
    {
#if BOARD_MSC == 3
        sprintf(apf_eeprom.logIsus, "//ISUS//PORK%04lu.log", (uint32_t)testNum);

        //sprintf(&apf_eeprom.logIsus[0],"ISUS//PORK%04lu.log", apf_fram.profileCnt);
        uprintf("Test 0: writing a big file, so big, something will break.  Filename = %s", apf_eeprom.logIsus); prn_nl();
#endif // BOARD_MSC == 3

        time_struct = get_RTC_time();
        strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", time_struct);

        tickStart = Timer_1ms();
        tickDelta = tickStart;

        onoff = 0;
        for(indx=0; indx<100000000; indx++)     //100 million
        {
            //ticks = Timer_1ms();
            //tickDelta = ticks - tickDelta;

            tickDelta = Timer_1ms() - tickDelta;
            ticks = (Timer_1ms() - tickStart);

            //if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCalc TempC   = %5u,   %5u", tickDelta, ticks);
    //        tickDelta = ticks;

            if((indx+1)%10 == 0 )
            {
                if(onoff == 0)
                {
                    onoff = 1;
                    pcal6416_lamp_pwr(1);
                    delay_msec(1);
                    pcal6416_deuterium(1);
                }
                else
                {
                    onoff = 0;
                    pcal6416_lamp_pwr(0);
                    pcal6416_deuterium(0);
                }
                wait_consoleTx();
                //prn_nl();
            }
            else
            {
                //uprintf(",");
            }

            ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
            ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current

            if(bme280_read_data() == 1)
            {
                fBrdPressure = bme280_get_fPressure();
                fBrdTempC = bme280_get_fTemperature();
                uiHumidity = bme280_get_uiHumidity();
            }
            else
            {
                fBrdPressure = NAN;
                fBrdTempC = NAN;
                uiHumidity = 0;
            }

#if BOARD_MSC == 3
            sprintf((char*)prnBuf, "%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, s=%u, t=%u,  %u \r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, apf_fram.logIsus_fsize, tickDelta, (ticks - tickStart)/1000);
#else
            sprintf((char*)prnBuf, "%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, t=%u,  %u \r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, tickDelta, (ticks - tickStart)/1000);
#endif

            //uprintf("%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, %u\r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, apf_fram.logIsus_fsize);
            tickDelta = ticks;
            if((indx+1)%5 == 0 )
                uprintf("%s", prnBuf);
#if BOARD_MSC == 3
            appendIsusProfileDataToSDCard((char*)prnBuf);      //exercise the fxn for writing ISUS log
#endif // #if BOARD_MSC == 3
            if(kbhit())
            {
                uprintf("DBG: kbhit()"); prn_nl();
                onoff = 0;
                pcal6416_lamp_pwr(0);
                pcal6416_deuterium(0);
                //break;
                return;
            }

        }  // for indx
    }
    else if(testNum == 1)
    {

#if BOARD_MSC == 3
        sprintf(apf_eeprom.logIsus, "//ISUS//PORK%04lu.log", (uint32_t)testNum);
        //sprintf(&apf_eeprom.logIsus[0],"ISUS//PORK%04lu.log", apf_fram.profileCnt);

        uprintf("Test 1: writing a big file, so big, something will break. no i2c, no sprintf  Filename = %s, ctrl-c or Q to stop", apf_eeprom.logIsus); prn_nl();
#endif

        time_struct = get_RTC_time();
        strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", time_struct);

        ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
        ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current

        if(bme280_read_data() == 1)
        {
            fBrdPressure = bme280_get_fPressure();
            fBrdTempC = bme280_get_fTemperature();
            uiHumidity = bme280_get_uiHumidity();
        }
        else
        {
            fBrdPressure = NAN;
            fBrdTempC = NAN;
            uiHumidity = 0;
        }

#if BOARD_MSC == 3
        sprintf((char*)prnBuf, "%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, s=%u, t=%u,  %u \r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, apf_fram.logIsus_fsize, tickDelta, (ticks - tickStart)/1000);
#else
        sprintf((char*)prnBuf, "%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, t=%u,  %u \r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, tickDelta, (ticks - tickStart)/1000);
#endif
        //uprintf("%07u Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u, Pressure = %6.3f, %u, %u\r\n", indx, fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity, fBrdPressure, onoff, apf_fram.logIsus_fsize);

        tickStart = Timer_1ms();
        tickDelta = tickStart;

        onoff = 0;
        for(indx=0; indx<100000000; indx++)     //100 million
        {
            //tickDelta = Timer_1ms() - tickDelta;
            //ticks = (Timer_1ms() - tickStart);
            ticks = Timer_1ms();
#if BOARD_MSC == 3
            appendIsusProfileDataToSDCard((char*)prnBuf);      //exercise the fxn for writing ISUS log
#endif // #if BOARD_MSC == 3
            tickDelta = Timer_1ms() - ticks;

            if(indx%10 == 0)
            {
                if(indx%10000 == 0)
                {
                    prn_nl();
#if BOARD_MSC == 3
                    uprintf("logIsus_fsize = %u", apf_fram.logIsus_fsize);
                    prn_nl();
#endif
                }
#if BOARD_MSC == 3
                if((apf_fram.logIsus_fsize % 1000000) == 0)
                {
                    uprintf("filesize = %u MB", (apf_fram.logIsus_fsize/1000000)); prn_nl();
                }
                uprintf("[%07u, %07u]\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", indx, tickDelta);
#endif

                //if(kbhit())  // had to hit character twice
                if( UARTRxBytesAvail() )
                {
                    inKey = getChar();
#define KEY_CTRL_C     0x03
                    if(inKey == 'Q' || inKey == 'q' || inKey == KEY_CTRL_C)
                    {
                        uprintf("DBG: kbhit()"); prn_nl();
                        onoff = 0;
                        pcal6416_lamp_pwr(0);
                        pcal6416_deuterium(0);
                        //break;
                        return;
                    }

                }
            }

        }  // for indx
    }



    // Test 2:

    ina260_system_voltageCurrent( &sampl.fVbat_main, &sampl.fsysAmp );        //system voltage and current
    ina260_lamp_voltageCurrent( &sampl.fVbat_main, &sampl.fsysAmp );

    led_green_on();

    wdog_pet();

    tickStart = Timer_1ms();
    tickDelta = tickStart;

    //delay_msec(200);

    tickDelta = Timer_1ms() - tickDelta;
    ticks = (Timer_1ms() - tickStart);
    prn_nl();
    uprintf("Time         = %5u,   %5u", tickDelta, ticks);  prn_nl();
    //tickDelta = ticks;

    //uprintf("Test for TEST dir...\r\n");
    fresult = f_stat("TEST3", &fno);
    switch (fresult)
    {

        case FR_OK:
//#ifdef NOCODE
            uprintf("Size: %lu\r\n", fno.fsize);
            uprintf("Timestamp: %u-%02u-%02u, %02u:%02u\r\n",
                   (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,
                   fno.ftime >> 11, fno.ftime >> 5 & 63);
            uprintf("Attributes: %c%c%c%c%c\r\n",
                   (fno.fattrib & AM_DIR) ? 'D' : '-',
                   (fno.fattrib & AM_RDO) ? 'R' : '-',
                   (fno.fattrib & AM_HID) ? 'H' : '-',
                   (fno.fattrib & AM_SYS) ? 'S' : '-',
                   (fno.fattrib & AM_ARC) ? 'A' : '-');
//#endif
            break;

        case FR_NO_FILE:
        case FR_NO_PATH:
            uprintf("TEST directory does not exist, creating.\r\n");
            fresult = f_mkdir("TEST3");
            if (fresult != FR_OK)
            {
                uprintf("Failed to create TEST3. (%d)", fresult);
                prn_nl();
            }
            break;

        default:
            uprintf("ERR: An error occurred f_stat DURA directory. (%d)\r\n", fresult);
            break;
    }



    fileCnt = 0;
    tickDelta = tickStart;
    for(fileCnt=0; fileCnt<10000; fileCnt++)  //10000
    {
        wait_consoleTx();
        if(kbhit())
        {
            uprintf("DBG: fileCnt kbhit()"); prn_nl();
            break;
        }
        sprintf(fileName, "TEST3\\pooBear%04u.txt", fileCnt);

        tickDelta = Timer_1ms() - tickDelta;
        ticks = (Timer_1ms() - tickStart);
        //tickDelta = ticks;
        uprintf("%s    ", fileName);
        uprintf("deltaTime = %5u, totTime = %5u", tickDelta, ticks);  prn_nl();


        //timeVal = read_hib_rtcc();
        time_struct = get_RTC_time();
        strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", time_struct);

        //fresult = f_open(&fTest, fileName, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
        fresult = f_open(&fTest, fileName, FA_READ | FA_WRITE | FA_OPEN_APPEND);

        if(fresult != FR_OK)
        {
            // != FR_OK
            uprintf("f_open error: %s", StringFromFresult(fresult));  prn_nl();
            //error_store("f_open error");

        }
        else  // FILE is open
        {

            // Seek to the end, to append our file
            fresult = f_lseek(&fTest, f_size(&fTest));
            if(fresult != FR_OK)
            {
                //uprintf("f_lseek error: %s", StringFromFresult(fresult));  prn_nl();
            }
            else
            {

                //pcal6416_spec_pwr(1);
                //pcal6416_lamp_pwr(1);
                //pcal6416_deuterium(1);
                //pcal6416_tungsten(1);

                ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
                ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current

                // fLampStartVolt = fLampVolt;
                // fLampStartAmp = fLampAmp;

                if(bme280_read_data() == 1)
                {
                    fBrdPressure = bme280_get_fPressure();
                    fBrdTempC = bme280_get_fTemperature();
                    uiHumidity = bme280_get_uiHumidity();
                }
                else
                {
                    fBrdPressure = NAN;
                    fBrdTempC = NAN;
                    uiHumidity = 0;
                }


                prn_nl();
                prn_nl();
                pcal6416_lamp_pwr(1);
                uprintf("DBG: measurement of Lamp voltage rise time."); prn_nl();
                for(indx=0; indx<1000; indx++)
                {
                    ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
                    ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current
                    //uprintf("Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u\r\n", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity);

                    if(fLampVolt > 0.0 && fLampVolt <= 3.0)
                    {
                        uprintf("1 ", indx);
                    }
                    else if (fLampVolt > 3.0 && fLampVolt <= 6.0)
                    {
                        uprintf("2 ", indx);
                    }
                    else if (fLampVolt > 6.0 && fLampVolt <= 9.0)
                    {
                        uprintf("3 ", indx);
                    }
                    else if (fLampVolt > 9.0 && fLampVolt <= 11.8)
                     {
                         uprintf("4 ", indx);
                     }
                    else
                    {
                        prn_nl();
                        uprintf("LampV = %7.3f, LampI = %7.3f %u ", fLampVolt, fLampAmp, indx); prn_nl();
                        break;
                    }
                    delay_msec(1);
                    //wait_consoleTx();
                }
                //pcal6416_lamp_pwr(0);
                //Results: 1 1 4 or 1 2 4 which indicates Lamp power supply is at 12v within 5msec
                wait_consoleTx();

                //Lamp 12v is powered up, turn on Deuterium now
                pcal6416_deuterium(1);
                uprintf("DBG: measurement of Deuterium Current rise time."); prn_nl();
                for(indx=0; indx<1000; indx++)
                {
                    ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
                    ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current
                    //uprintf("Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u\r\n", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity);


                    if(fLampAmp > 0.0 && fLampAmp <= 0.1)
                    {
                        uprintf("5 ", indx);
                    }
                    else if (fLampAmp > 0.1 && fLampAmp <= 0.2)
                    {
                        uprintf("6 ", indx);
                    }
                    else if (fLampAmp > 0.2 && fLampAmp <= 0.3)
                    {
                        uprintf("7 ", indx);
                    }
                    else if (fLampAmp > 0.3 && fLampAmp <= 0.32)
                     {
                         uprintf("8 ", indx);
                     }
                    else
                    {
                        prn_nl();
                        uprintf("LampV = %7.3f, LampI = %7.3f %u ", fLampVolt, fLampAmp, indx); prn_nl();
                        break;
                    }
                    delay_msec(1);
                    //wait_consoleTx();
                }


                pcal6416_deuterium(1);
                pcal6416_lamp_pwr(0);


                //delay_msec(2000);


                for(indx=0; indx<20000; indx++)  //20000
                {
                    pcal6416_lamp_pwr(1);
                    for(jndx=0; jndx<10000; jndx++)
                    {
                        wait_consoleTx();

                        ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
                        ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //Lamp voltage and current

                        sprintf((char*)prnBuf, "Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u\r\n", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity);
                        uprintf("Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u\r\n", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity);


                        // Write the formatted data string in the prnBuf buffer to the SD card
                        fresult = f_write( &fTest, prnBuf, strlen((char *)prnBuf), &bw);
                        if(fresult != FR_OK)
                        {
                            uprintf("f_write error: %s\r\n", StringFromFresult(fresult));
                            break;
                        }

                        if(fLampVolt > 11.5)
                        {
                            uprintf("Lamp Volts = %7.3f, jndx = %u", fLampVolt, jndx); prn_nl();

                            break;
                        }

                        delay_msec(1);
                    }
                    pcal6416_lamp_pwr(0);

                    sprintf((char*)prnBuf, "Vin = %6.3f, Iin = %6.3f, Vlamp = %6.3f, Ilamp = %6.3f, BrdTempC = %5.2f, Humidity = %u\r\n", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, uiHumidity);

                    // Write the formatted data string in the prnBuf buffer to the SD card
                    fresult = f_write( &fTest, prnBuf, strlen((char *)prnBuf), &bw);
                    if(fresult != FR_OK)
                    {
                        uprintf("f_write error: %s\r\n", StringFromFresult(fresult));
                    }
                    else
                    {
                        //uprintf("\r\nSampling... f_write is good\r\n"); // THOM DEBUG
                    }

                    if( UARTRxBytesAvail() )        // replace wiht kbhit()
                    {
                        //input = get_key();
                        inKey = getChar();

                        uprintf("getChar = %c, 0x%02x", inKey, inKey); prn_nl();

                        break;

                    }

                    if(kbhit())
                    {
                        inKey = getChar();

                        uprintf("getChar = %c, 0x%02x", inKey, inKey); prn_nl();

                        uprintf("DBG: indx kbhit()"); prn_nl();
                        break;
                    }
                }

            }

            // Close the file
            fresult = f_close( &fTest );
            if(fresult != FR_OK)
            {
                uprintf("f_close error: %s", StringFromFresult(fresult));  prn_nl();
                //error_store("f_close error");
            }

        }
    } // for fileCnt
}


void timebase_test(void)
{
    led_red_off();
    int i;
    for(i = 0; i < 10; i++)
    {
        #if 0
            delay_msec_coarse(100);
        #else
            delay_msec(100);
        #endif

        led_red_on();

        #if 0
            delay_msec_coarse(100);
        #else
            delay_msec(100);
        #endif

        led_red_off();
    }

    led_red_off();
}

void thomzone_nand_test(void)
{
    uint8_t mfgId = 0;
    uint8_t chipId = 0;
    uint8_t BLOCK_LOCK = 0;
    uint8_t CONFIG = 0;
    uint8_t STATUS = 0;
    uint8_t DIE_SEL = 0;
    Bad_Block_Set bb_set;
    int i = 0;
    uint32_t chipNum = 0;

    spi_flash_open();

    if(spi_flash_cmd_readId(chipNum, &mfgId, &chipId))
    {
        uprintf("spi_flash_readId failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_readId mfgId: 0x%02X, chipId: 0x%02X", mfgId, chipId); prn_nl();
    }

    if(spi_flash_probe(chipNum))
    {
        uprintf("spi_flash_probe failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_probe ok"); prn_nl();
    }

    if(spi_flash_cmd_getFeature(chipNum, MT29F_FEATURE_ADDR_BLOCK_LOCK, &BLOCK_LOCK))
    {
        uprintf("spi_flash_getFeature failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_getFeature BLOCK_LOCK: 0x%02X", BLOCK_LOCK); prn_nl();
    }

    if(spi_flash_cmd_getFeature(chipNum, MT29F_FEATURE_ADDR_CONFIG, &CONFIG))
    {
        uprintf("spi_flash_getFeature failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_getFeature CONFIG: 0x%02X", CONFIG); prn_nl();
    }

    if(spi_flash_cmd_getFeature(chipNum, MT29F_FEATURE_ADDR_STATUS, &STATUS))
    {
        uprintf("spi_flash_getFeature failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_getFeature STATUS: 0x%02X", STATUS); prn_nl();
    }

    if(spi_flash_cmd_getFeature(chipNum, MT29F_FEATURE_ADDR_DIE_SEL, &DIE_SEL))
    {
        uprintf("spi_flash_getFeature failed"); prn_nl();
    }
    else
    {
        uprintf("spi_flash_getFeature DIE_SEL: 0x%02X", DIE_SEL); prn_nl();
    }

    if( spi_flash_readBadBlockSet(chipNum, &bb_set) )
    {
        uprintf("spi_flash_readBadBlockSet failed"); prn_nl();
    }
    else
    {
        for(i = 0; i < (MT29F4G01_FLASH_BLOCK_CNT/32); i++)
        {
            if(bb_set.bad_blocks[i])
            {
                uprintf("spi_flash_readBadBlockSet[%02d]: 0x%08X BAD", i, bb_set.bad_blocks[i]); prn_nl();
            }
            else
            {
                uprintf("spi_flash_readBadBlockSet[%02d]: 0x%08X OK", i, bb_set.bad_blocks[i]); prn_nl();
            }

            wait_consoleTx();
        }
    }

    uprintf("Flash test done"); prn_nl();
}

int thomzone_fatfs_write_file(char const * const fname, uint8_t const * const data, const size_t len)
{
    FIL fileObject;     // File object
    FRESULT fresult;
    UINT bw;            // Bytes written
    int retVal = 1;
    bool is_open = false;

    if( ! microSD_flg )
    {
        return 0;
    }

    memset(&fileObject, 0, sizeof(fileObject));

    do
    {
        // Open a file
        fresult = f_open(&fileObject, fname, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_open error: %s\r\n", StringFromFresult(fresult));
            break;
        }
        is_open = true;

        // Sync to shrink critical section
        fresult = f_sync(&fileObject);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_sync error: %s\r\n", StringFromFresult(fresult));
            break;
        }

        // Write the buffer to the SD card
        fresult = f_write(&fileObject, data, len, &bw);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_write error: %s\r\n", StringFromFresult(fresult));
            break;
        }

        // Sync to shrink critical section
        fresult = f_sync(&fileObject);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_sync error: %s\r\n", StringFromFresult(fresult));
            break;
        }

        if(len != bw)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_write error: write underrun\r\n");
            break;
        }
    } while(false);

    if(is_open)
    {
        // Close the file
        fresult = f_close(&fileObject);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_close error: %s\r\n", StringFromFresult(fresult));
        }
    }

    return retVal;
}

int thomzone_fatfs_read_verify_file(char const * const fname, uint8_t const * const data, const size_t len)
{
    FIL fileObject;     // File object
    FRESULT fresult;
    int retVal = 1;
    bool is_open = false;
    uint8_t readback[128];
    size_t data_offset = 0;

    if( ! microSD_flg )
    {
        return 0;
    }

    memset(&fileObject, 0, sizeof(fileObject));

    // Open a file
    fresult = f_open(&fileObject, fname, FA_READ | FA_OPEN_EXISTING);
    if(fresult != FR_OK)
    {
        retVal = 0;
        uprintf("thomzone_fatfs_read_verify_file f_open error: %s\r\n", StringFromFresult(fresult));
    }
    else
    {
        is_open = true;
    }

    if(is_open)
    {
        do
        {
            UINT num_read;
            const size_t num_left    = len - data_offset;
            const size_t num_to_read = (128 < num_left) ? (128) : (num_left);
            fresult = f_read(&fileObject, readback, num_to_read, &num_read);
            if(fresult != FR_OK)
            {
                retVal = 0;
                uprintf("thomzone_fatfs_read_verify_file f_read error: %s\r\n", StringFromFresult(fresult));
                break;
            }

            if(memcmp(data + data_offset, readback, num_read) != 0)
            {
                retVal = 0;
                uprintf("thomzone_fatfs_read_verify_file memcmp error\r\n");
                break;
            }

            data_offset += num_read;

        } while(data_offset < len);
    }

    if(is_open)
    {
        // Close the file
        fresult = f_close(&fileObject);
        if(fresult != FR_OK)
        {
            retVal = 0;
            uprintf("thomzone_fatfs_write_file f_close error: %s\r\n", StringFromFresult(fresult));
        }
    }

    return retVal;
}

void thomzone_fatfs_lfn_test(void)
{
    const uint16_t test_vector_len = 521;
    uint8_t test_vector[521];

    // 255 chars
    const char* lfn_max_name = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF";
    // 254 chars
    const char* lfn_max_name_m1 = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDE";
    // 253 chars
    const char* lfn_max_name_m2 = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCD";

    // pseudo random test vector
    {
        crc8_t temp = crc8_init();
        int i;
        for(i = 0; i < test_vector_len-1; i++)
        {
            temp = crc8_update(temp, &i, sizeof(i));
            test_vector[i] = temp;
        }
        test_vector[test_vector_len-1] = crc8_finalize(temp);
    }


    // SFN test
    if(thomzone_fatfs_write_file("1234.x", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_write_file("12345678.bin", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    // LFN test
    if(thomzone_fatfs_write_file("123456789ABCDEF.data.u8bin", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_write_file(lfn_max_name, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_write_file(lfn_max_name_m1, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_write_file(lfn_max_name_m2, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    // readback SFN
    if(thomzone_fatfs_read_verify_file("1234.x", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_read_verify_file("12345678.bin", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    // readback LFN
    if(thomzone_fatfs_read_verify_file("123456789ABCDEF.data.u8bin", test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_read_verify_file(lfn_max_name, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test lfn_max_name failed, known bug in FatFS\r\n");
    }

    if(thomzone_fatfs_read_verify_file(lfn_max_name_m1, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    if(thomzone_fatfs_read_verify_file(lfn_max_name_m2, test_vector, test_vector_len) <= 0)
    {
        uprintf("thomzone_fatfs_lfn_test failed\r\n");
        return;
    }

    uprintf("thomzone_fatfs_lfn_test pass\r\n");
}



void thomzone_ds3231_test(void)
{
    DS3231_DateTime t0;
    uint16_t temp0;
    memset(&t0, 0, sizeof(t0));
    bool osc_stopped = false;

    if(ds3231_init(false, false, &osc_stopped) <= 0)
    {
        uprintf("ds3231_init error"); prn_nl();
        return;
    }
    else
    {
        uprintf("ds3231_init ok"); prn_nl();
    }

    if(osc_stopped)
    {
        uprintf("ds3231_init says osc_stopped"); prn_nl();
    }

    if(ds3231_get_time(&t0) <= 0)
    {
       uprintf("ds3231_get_time error"); prn_nl();
       return;
    }
    else
    {
       uprintf("ds3231_get_time ok"); prn_nl();
       uprintf("Time: %s, %04d-%02d-%02d %02d:%02d:%02d",
               DS3231_DAY_OF_WEEK_to_string((DS3231_DAY_OF_WEEK)t0.day_of_week),
               t0.year,
               t0.month,
               t0.day_of_month,
               t0.hours,
               t0.minutes,
               t0.seconds
       );
       prn_nl();
    }

    if(ds3231_get_temp(&temp0) <= 0)
    {
       uprintf("ds3231_get_temp error"); prn_nl();
       return;
    }
    else
    {
       uprintf("Temp: %.2f", ds3231_temp_reg_to_degC(temp0)); prn_nl();
    }
}

void thomzone_read_rtcc()
{
    const time_t t_i = read_hib_rtcc();
    uint32_t u32time;
    uint64_t t_idx;

    uprintf("daylight: %d\r\n", _tz.daylight);
    uprintf("timezone: %d\r\n", _tz.timezone);
    uprintf("tzname: %s\r\n",   _tz.tzname);
    uprintf("dstname: %s\r\n",  _tz.dstname);

    u32time = 0;
    struct tm * time_info = gmtime(&u32time);
    uprintf("gmtime(0): %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    u32time = 0;
    time_info = localtime(&u32time);
    uprintf("localtime(0): %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    u32time = TI_CCS_UNIX_EPOCH_OFFSET;
    time_info = gmtime(&u32time);
    uprintf("gmtime(TI_CCS_UNIX_EPOCH_OFFSET): %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    u32time = TI_CCS_UNIX_EPOCH_OFFSET;
    time_info = localtime(&u32time);
    uprintf("localtime(TI_CCS_UNIX_EPOCH_OFFSET): %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    time_info = gmtime(&t_i);
    uprintf("gmtime: %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    time_info = localtime(&t_i);
    uprintf("localtime: %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    const time_t t_roll = 0xFFFFFFFFUL;
    time_info = gmtime(&t_roll);
    uprintf("gmtime of rollover: %02d/%02d/%04d %02d:%02d:%02d\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec
            );

    for(t_idx = 0xFFFFFFFCULL; t_idx <= 0xFFFFFFFFULL; t_idx++)
    {
        time_t time_t_i = t_idx & 0xFFFFFFFFUL;
        time_info = gmtime(&time_t_i);
        uprintf("gmtime of 0x%08X: %02d/%02d/%04d %02d:%02d:%02d\r\n",
                time_t_i,
                time_info->tm_mon  + 1,
                time_info->tm_mday,
                time_info->tm_year + 1900,
                time_info->tm_hour,
                time_info->tm_min,
                time_info->tm_sec
                );

        wait_consoleTx();
    }

    //uprintf("DBG: get_RTC_time"); prn_nl();
    time_info = get_RTC_time();                           //populate time struct
    u32time = read_hib_rtcc();       //read integer time, APFTODO, check time
    uprintf("get_RTC_time: %02d/%02d/%04d %02d:%02d:%02d ti_time %u, unix_time %u\r\n",
            time_info->tm_mon  + 1,
            time_info->tm_mday,
            time_info->tm_year + 1900,
            time_info->tm_hour,
            time_info->tm_min,
            time_info->tm_sec,
            u32time,
            u32time - TI_CCS_UNIX_EPOCH_OFFSET
            );

    struct tm unix_epoch_tm =
    {
     .tm_sec   = 0,  /* seconds after the minute   - [0,59]  */
     .tm_min   = 0,  /* minutes after the hour     - [0,59]  */
     .tm_hour  = 0,  /* hours after the midnight   - [0,23]  */
     .tm_mday  = 1,  /* day of the month           - [1,31]  */
     .tm_mon   = 0,  /* months since January       - [0,11]  */
     .tm_year  = 70, /* years since 1900                     */
     .tm_wday  = 4,  /* days since Sunday          - [0,6]   */
     .tm_yday  = 0,  /* days since Jan 1st         - [0,365] */
     .tm_isdst = 0,  /* Daylight Savings Time flag           */
    };
    const time_t unix_epoch_s = mktime(&unix_epoch_tm);

    uprintf("TI_CCS_UNIX_EPOCH_OFFSET: 0x%08X\r\n", unix_epoch_s);
}

void thomzone_reset_fram_eeprom()
{
    uint32_t ret = MAP_EEPROMMassErase();
    if(ret != 0)
    {
        uprintf("MAP_EEPROMMassErase failed\r\n");
    }

    uint8_t data[128];
    memset(data, 0xFF, 128);

    // MB85RC1MT is 128KiB
    int i;
    for(i = 0; i < 1024UL; i++)
    {
        const uint32_t base_addr = i * 128UL;
        if(i2c0_fram_write(0x50, data, 128, base_addr) <= 0)
        {
            uprintf("i2c0_fram_write failed at address: 0x%08X\r\n", base_addr);
            break;
        }
    }
}

void thom_zone_menu(void)
{
    time_t timer;
    char input;

    while(1)
    {

        prn_nl();  prn_nl(); uprintf("Test Menu:");  prn_nl();

        uprintf("1 -- Nitrate sample, apf_take_sample_nitrate()"); prn_nl();
        uprintf("2 -- i2c0 probe"); prn_nl();
        uprintf("3 -- pcal6416 test"); prn_nl();
        uprintf("4 -- pH sample, apf_take_sample_pH()"); prn_nl();
        uprintf("5 -- mms bake 10 sec"); prn_nl();
        uprintf("6 -- spi flash test"); prn_nl();
        uprintf("7 -- fram soak test"); prn_nl();
        uprintf("8 -- bme280 soak test"); prn_nl();
        uprintf("A -- ina260 soak test"); prn_nl();
        uprintf("B -- nand test"); prn_nl();
        uprintf("C -- ds3231 test"); prn_nl();
        uprintf("D -- timebase led blink test"); prn_nl();
        uprintf("E -- FatFS LFN test"); prn_nl();
        uprintf("F -- RTCC time test"); prn_nl();
        uprintf("G -- Reset EEPROM and FRAM"); prn_nl();
        uprintf("9 -- Exit to Main Menu"); prn_nl();
        prn_nl(); uprintf("Enter Selection [9]: ");

        UARTFlushRx();
        timer = MENU_TIMEOUT_SEC + read_hib_rtcc();

        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                input = get_key();
                break;
            }

            if( timer <= read_hib_rtcc() ) sleep(IDLE, 0);
        }

        switch(input)
        {
#if BOARD_MSC == 3
            case '1':
                apf_take_sample_nitrate(1);     //txflg=1, mms_IsusLampTest();     // mms_test.c
                break;

            case '4':
                apf_take_sample_pH(1);      //mms_test_menu();        //mms_spec.c
                break;
#endif // if BOARD_MSC == 3

            case '2':
                i2c0_probe();   //i2c0.c
                break;

            case '3':
                pcal6416_test_menu();
                break;

#if BOARD_MSC == 3
            case '5':
                mms_bake_handler(10);        //mms_spec.c
                break;
#endif // BOARD_MSC == 3
            case '6':
                spi_flash_test(0);        //spi_flash.c
                break;

#if BOARD_MSC == 3
            case '7':
                fram_soak_test();
                break;
#endif

            case '8':
                bme280_soak_test();
                break;

            case 'A':
            case 'a':
                ina260_soak_test();
                break;

            case 'B':
            case 'b':
                thomzone_nand_test();
                break;

            case 'C':
            case 'c':
                thomzone_ds3231_test();
                break;

            case 'D':
            case 'd':
                timebase_test();
                break;

            case 'E':
            case 'e':
                thomzone_fatfs_lfn_test();
                break;

            case 'F':
            case 'f':
                thomzone_read_rtcc();
                break;

            case 'G':
            case 'g':
                thomzone_reset_fram_eeprom();
                break;

            default: //Exit
                prn_nl(); uprintf("Press enter to bring up main menu");  prn_nl();
                return;
        }

    }

}


