   /// CONFIG_VARS.C

#include <stdlib.h>     /// atoi
#include <stdio.h>      /// sprintf
#include "cwgconf.h"
#include "ui.h" /// uistrName
#include "ff.h"
#include "script.h"
#include "iowfile.h"
#include "licor8xx-shared.h"

/// __LOCAL DEFINES__*************************************

#define CFG_BUFF_BYTES 32

/// String-Value pair for configuration variables
typedef struct CfgVar_s{
    const char* name;   ///<Name of variable(must be all caps)
    UINT16 value;       ///<Value (non-negative int))

//    byte type;// or sscanf format
//    union{
//    UINT16 value;       ///<Value (non-negative int))
//    float fval;
//    char sval[16];
//    };
}

CfgVar;
/// __LOCAL DATA***************************************
/// Table of all variables and their default values
/// also the new values for them are held
CfgVar VarTbl[] = {
    {CFG_SERIAL_N, .value=0},   ///<serial number, printed at boot
    {CFG_VERBOSE, 0},       ///<less output to user
    {CFG_ECHO, ECHO_ALL},        ///<echo serial input(0=none, 1=all, 2=CONS only)
    {CFG_LICOR_TASK_EN, 0},    ///<enable licor task at start
    {CFG_UW_TASK_EN, 0}, ///<enable underway at start
#ifdef WITH_PH
    {CFG_PH_TASK_EN, 0},       ///<enable pH task at start
    {CFG_PH_PERIOD_MIN, 10},     ///<pH interval in min
    {CFG_PH_SCALE, 2},    ///<scaling for pH readout
#endif
#ifdef WITH_OXY
    {CFG_O2_TASK_EN, 0},      ///<enable aanderaa oxy at start
    {CFG_O2_PERIOD_MIN, 10},    ///<oxy interval in min
#endif
#ifdef WITH_TELEM
    {CFG_ORBCOMM_EN, 0},  ///<enable Orbcomm task at start
    {CFG_TEL_MODE, 0},  ///<0=nothing, 1=WG packet, 2=OA packet
    {CFG_TEL_PERIOD_MIN, 60},  ///<orbcomm interval in min
#endif
    {CFG_POWER_INIT, 0},   ///<bits field of outputs to come on at startup
    {CFG_UW_RATE, 2},     ///<underway sample rate
    {CFG_UW_PERIOD_MIN, 0},      ///<underway air/zero/std period (min)
    {CFG_TEL_XMIT_MIN, 10}, ///<min to wait for messages to get out
    {CFG_TEL_RETRIES, 2},  ///<# of times to wait
    {CFG_LICOR_PERIOD_MIN, 60},     ///<licor interval in minutes
    {CFG_VALVE_TYPE, 0},   ///<valve setting, 0 = block, 1 = manifold
    {CFG_LICOR_MODEL, LICOR_MODEL_DFL},  ///<820:LI820 830:LI830 840:LI840 850:LI850
    {CFG_STD_MOD, 0},     ///<how many runs between CO2 standards
    {CFG_EQU_SEC, 300},   ///<equilibration time
    {CFG_AIR_SEC, 60},      ///<air sample time
    {CFG_ZERO_SEC, 60},     ///<zero time
    {CFG_STD_SEC, 60},      ///<standard flow time
    {CFG_PURGE_SEC, 30},    ///<equilibrator pump-out time
    {CFG_VENT_SEC, 120},    ///<equilibrator vent time
    {CFG_AUTO_OUT,0},     ///<print data when finished with sample
    {"\0", 0}           ///<end of list
};

/// Opens #CFG_FILE and reads each line. Parses name-value pairs and stores
/// new values in the #VarTbl
return_code_t cfgLoad()
{
    return_code_t retval=ERR_FAILURE;
#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s, *fp=&file_s;
#else
    FILE_T *fp;
#endif
    int i;
    char *ptr;
    long fill;
    char buff[CFG_BUFF_BYTES];

    if(iow_fopen(&fp,SYS_CONFIG_DFL, IOW_READ)==0){
        while (iow_fgets(fp, buff, CFG_BUFF_BYTES)) {
            for (i = 0; '\0' != *VarTbl[i].name; i++) {
                ptr = uiStrName(buff, VarTbl[i].name);
                if (NULL != ptr) {
                    fill = atoi(ptr);
                    VarTbl[i].value = (UINT16) fill;
                    break;
                }
            }
        }
        retval=ERR_SUCCESS;
    }

    return retval;
}

return_code_t cfgGet(char* str, UINT16 *value) {
    int i;
    for (i = 0; '\0' != *VarTbl[i].name; i++) {
        if (NULL != uiStrName(str, VarTbl[i].name)) {
            *value = VarTbl[i].value;
            return (ERR_SUCCESS);
        }
    }
    return (ERR_FAILURE);
}

int cfgIsZero(char* str){
    int i;
    for (i=0; '\0'!= *VarTbl[i].name; i++){
        if(NULL != uiStrName(str, VarTbl[i].name)){
            if(0==VarTbl[i].value){
                return TRUE;
            }else{
                return (FALSE);
            }
        }
    }
    return(FALSE);
}

return_code_t cfgSet(char* str, UINT16 value) {
    int i=0;
    for (i = 0; '\0' != *VarTbl[i].name; i++) {
        if (NULL != uiStrName(str, VarTbl[i].name)) {
            VarTbl[i].value = value;
            return (ERR_SUCCESS);
        }
    }
    return (ERR_FAILURE);

}

return_code_t cfgSave(void)
{
    return_code_t retval=ERR_FAILURE;

 #if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s, *cfgFile=&file_s;
#else
    FILE_T *cfgFile;
#endif

    int i;
    char buff[CFG_BUFF_BYTES];

    if(iow_fopen(&cfgFile, "CONFIG.TXT", IOW_WRITE | IOW_CREATE_ALWAYS)==0) {
        for (i = 0; '\0' != *VarTbl[i].name; i++) {
            sprintf(buff,"%s %d\r\n",VarTbl[i].name, VarTbl[i].value);
            iow_fputs(cfgFile,buff);
        }
        //truncate file needed?
        iow_fclose(cfgFile);
        retval=ERR_SUCCESS;
    }

    return retval;
}

/// Most useful if called like \code while(ERR_FAILURE!=cfgDumpLine(s)){print(s)}

 ///\endcode
/// to print full set of variables.
return_code_t cfgDumpLinew(char* line,int kw, int vw){
    static int i;

    if(*VarTbl[i].name == '\0'){
        i=0;
        return(ERR_FAILURE);
    }
    sprintf(line, "%*s %*d\r\n", kw, VarTbl[i].name, vw, VarTbl[i].value);
    i++;
    return(ERR_SUCCESS);
}

// not sure we need mutex, since each main "thread"
// (script, console, host...) executes completely
// and yields
//static volatile int g_last_rec_mutex_inst=false;
//#define MUTEX_LOCK(x) mutex_lock(x)
//#define MUTEX_UNLOCK(x) mutex_unlock(x)
//
//bool test_and_set(volatile bool *mutex) {
//    // TODO: write atomic test/set
//    // (this implementation is psuedocode)
//   bool tmp=*mutex;
//   *mutex = true;
//   return tmp;
//}
//
//void mutex_init(volatile bool *mutex) {
//   *mutex = false;
//}
//
//void mutex_lock(volatile bool *mutex) {
//   while(test_and_set(mutex));
//}
//
//void mutex_unlock(volatile bool *mutex) {
//   *mutex = false;
//}

//enter_region:        ; A "jump to" tag; function entry point.
//
//  tsl reg, flag      ; Test and Set Lock; flag is the
//                     ; shared variable; it is copied
//                     ; into the register reg and flag
//                     ; then atomically set to 1.
//
//  cmp reg, #0        ; Was flag zero on entry_region?
//
//  jnz enter_region   ; Jump to enter_region if
//                     ; reg is non-zero; i.e.,
//                     ; flag was non-zero on entry.
//
//  ret                ; Exit; i.e., flag was zero on
//                     ; entry. If we get here, tsl
//                     ; will have set it non-zero; thus,
//                     ; we have claimed the resource
//                     ; associated with flag.
//
//leave_region:
//  move flag, #0      ; store 0 in flag
//  ret                ; return to caller
//void foo_lock(volatile byte *mutex){
//    asm ("swap %0" : "+r"(mutex));
//    asm("RETURN");
//}
