///
/// @file licor8xx-shared.h
/// @authors k. headley
/// @date 05 dec 2019

/// Shared (cross-platform) Licor 8xx data API
/// Licor 8xx gas analyzer driver for carbon waveglider

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2019 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys' fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

// Always do this
#ifndef LICOR8XX_SHARED_H
#define LICOR8XX_SHARED_H

/////////////////////////
// Includes
/////////////////////////
#include <stdbool.h>
#include <math.h>
#include "cwg-shared.h"
#include "sequence.h"

#ifdef WITH_LOG_UTILS
#if  defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
#include "ui.h"
#include "fileutils.h"
#endif // PICx
#endif // WITH_LOG_UTILS

/////////////////////////
// Macros
/////////////////////////

/// @def LICOR_MODEL_DFL
/// @brief default licor model number 
#define LICOR_MODEL_DFL LI_MODEL_850

#if 0
#define ZBLINE_N(pz,i)     (pz->mdata[i].stat_n)
#define ZBLINE_MY(pz,i)    (pz->mdata[i].M[ZID_CO2])
#define ZBLINE_SY(pz,i)    (pz->mdata[i].S[ZID_CO2])
#define ZBLINE_MX(pz,i)    (pz->mdata[i].X[ZID_MX])
#define ZBLINE_SX(pz,i)    (pz->mdata[i].X[ZID_SX])
#define ZBLINE_SXY(pz,i)   (pz->mdata[i].X[ZID_SXY])

/// @def ZBLINE_SDEV(pa)
/// @brief baseline standard deviation value
/// @param pa pointer to licor_brecord_t
/// @return licor_brecord_t standard deviation  value
#define ZBLINE_SDEV(pz,i) (double)sqrt( ((ZBLINE_N(pz,i) > 0) ? ZBLINE_SY(pz,i)/(ZBLINE_N(pz,i) - 1) : 0.0)  )
#define ZBLINE_SDEVY(pz,i) (double)sqrt( ((ZBLINE_N(pz,i) > 0) ? ZBLINE_SY(pz,i)/(ZBLINE_N(pz,i) - 1) : 0.0)  )
/// @def ZBLINE_VAR(pa)
/// @brief baseline variance value
/// @param ps pointer to zrecord state
/// @return licor_brecord_t variance  value
#define ZBLINE_VAR(pz,i)  ((pz->mdata[i].stat_n > 0) ? pz->mdata[i].S[ZID_CO2]/(pz->mdata[i].stat_n - 1) : 0.0 )

#define ZBLINE_SDEVX(pz,i) ( (ZBLINE_N(pz,i) > 0) ? (double)sqrt(ZBLINE_SX(pz,i)/(ZBLINE_N(pz,i) - 1) ) : 0.0)
#define ZBLINE_r(pz,i)     ( (ZBLINE_N(pz,i) > 0) ? (ZBLINE_SXY(pz,i)/(sqrt(ZBLINE_SY(pz,i))*sqrt(ZBLINE_SX(pz,i)))) : 0.0 )
#define ZBLINE_r2(pz,i)    ( (ZBLINE_N(pz,i) > 0) ? ((ZBLINE_SXY(pz,i)*ZBLINE_SXY(pz,i))/(ZBLINE_SY(pz,i)*ZBLINE_SX(pz,i))) : 0.0 )
#define ZBLINE_m(pz,i)     ( (ZBLINE_N(pz,i) > 0) ? (ZBLINE_r(pz,i)*ZBLINE_SDEVY(pz,i)/ZBLINE_SDEVX(pz,i)) : 0.0 )
#define ZBLINE_b(pz,i)     ( (ZBLINE_N(pz,i) > 0) ? (ZBLINE_MY(pz,i)-ZBLINE_m(pz,i)*ZBLINE_MX(pz,i)) : 0.0 )
#endif

/// @def ZMBLINE_N(pm)
/// @brief baseline sample size
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_N(pm)     (pm->stat_n)
/// @def ZMBLINE_MY(pm)
/// @brief baseline Y (CO2) mean
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_MY(pm)    (pm->M[ZID_CO2])
/// @def ZMBLINE_SY(pm)
/// @brief baseline Y (CO2) sqrt variance (S)
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_SY(pm)    (pm->S[ZID_CO2])
/// @def ZMBLINE_MX(pm)
/// @brief baseline X (time) mean
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_MX(pm)    (pm->X[ZID_MX])
/// @def ZMBLINE_SX(pm)
/// @brief baseline X (time) sqrt variance (S)
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_SX(pm)    (pm->X[ZID_SX])
/// @def ZMBLINE_SXY(pm)
/// @brief baseline XY sqrt covariance (SXY)
/// @param pa pointer to licor_brecord_t
#define ZMBLINE_SXY(pm)   (pm->X[ZID_SXY])

/// @def ZMBLINE_SDEV(pa)
/// @brief baseline Y standard deviation value
/// @param pa pointer to licor_brecord_t
/// @return licor_brecord_t Y standard deviation  value
#define ZMBLINE_SDEV(pm) (float)sqrt( ((ZMBLINE_N(pm) > 0) ? ZMBLINE_SY(pm)/(ZMBLINE_N(pm) - 1) : 0.0)  )
/// @def ZMBLINE_SDEVY(pa)
/// @brief baseline Y standard deviation value
/// @param pa pointer to licor_brecord_t
/// @return licor_brecord_t Y standard deviation  value
#define ZMBLINE_SDEVY(pm) (float)sqrt( ((ZMBLINE_N(pm) > 0) ? ZMBLINE_SY(pm)/(ZMBLINE_N(pm) - 1) : 0.0)  )
/// @def ZmBLINE_VAR(pa)
/// @brief baseline variance value
/// @param ps pointer to zrecord state
/// @return licor_brecord_t variance  value
#define ZMBLINE_VAR(pm)  ((pm->stat_n > 0) ? pm->S[ZID_CO2]/(pm->stat_n - 1) : 0.0 )

/// @def ZMBLINE_SDEVX(pm)
/// @brief baseline X standard deviation value
/// @param pa pointer to licor_brecord_t
/// @return licor_brecord_t X standard deviation  value
#define ZMBLINE_SDEVX(pm) ( (ZMBLINE_N(pm) > 0) ? (float)sqrt(ZMBLINE_SX(pm)/(ZMBLINE_N(pm) - 1) ) : 0.0)
/// @def ZMBLINE_r(pm)
/// @brief baseline correlation value (r)
/// @param pa pointer to licor_brecord_t
/// @return baseline correlation (r))
#define ZMBLINE_r(pm)     ( (ZMBLINE_N(pm) > 0) ? (ZMBLINE_SXY(pm)/(sqrt(ZMBLINE_SY(pm))*sqrt(ZMBLINE_SX(pm)))) : 0.0 )
/// @def ZMBLINE_r2(pm)
/// @brief baseline correlation value (r^2)
/// @param pa pointer to licor_brecord_t
/// @return baseline correlation (r^2)
#define ZMBLINE_r2(pm)    ( (ZMBLINE_N(pm) > 0) ? ((ZMBLINE_SXY(pm)*ZMBLINE_SXY(pm))/(ZMBLINE_SY(pm)*ZMBLINE_SX(pm))) : 0.0 )
/// @def ZMBLINE_m(pm)
/// @brief baseline slope
/// @param pa pointer to licor_brecord_t
/// @return baseline slope
#define ZMBLINE_m(pm)     ( (ZMBLINE_N(pm) > 0) ? (ZMBLINE_r(pm)*ZMBLINE_SDEVY(pm)/ZMBLINE_SDEVX(pm)) : 0.0 )
/// @def ZMBLINE_b(pm)
/// @brief baseline Y-intercept
/// @param pa pointer to licor_brecord_t
/// @return baseline Y-intercept
#define ZMBLINE_b(pm)     ( (ZMBLINE_N(pm) > 0) ? (ZMBLINE_MY(pm)-ZMBLINE_m(pm)*ZMBLINE_MX(pm)) : 0.0 )

/// @def XM_SDEV(pm,i)
/// @brief std deviation of ith field
/// @param pa pointer to licor_brecord_t
/// @param i  parameter index
/// @return baseline Y-intercept
#define XM_SDEV(pm,i) (float)sqrt( ((NULL!=pm) && (pm->stat_n > 0) ? pm->S[i]/(pm->stat_n - 1) : 0.0)  )
#define XM_SDEV_TO_S(pm,s) (float) ( (NULL!=pm) && (pm->stat_n > 0) ? (s*s)*(pm->stat_n-1) : 0.0)  

/////////////////////////
// Type Definitions
/////////////////////////

/// @def CYCLE_ID_INVALID
/// @brief cycle ID invalid. Out-of-cycle  ID
#define CYCLE_ID_INVALID -1
/// @def SEQN_INVALID
/// @brief sequence number invalid. Out-of-cycle sequence number
#define SEQN_INVALID -1

/// @typedef enum licor_model_id licor_model_id
/// @brief supported licor models
typedef enum{
    LI_MODEL_820=820,
    LI_MODEL_830=830,
    LI_MODEL_840=840,
    LI_MODEL_850=850,
}licor_model_id;

/// @enum licor_rtype_id
/// @brief record type ID
typedef enum{
    RTID_ZRECv0=0x1,
    RTID_INVALID=0xFFFF,
}licor_rtype_id;

/// @enum licor_rmeas_id
/// @brief record meas ID
typedef enum{
    RMID_NONE=0,
    RMID_SAMPLE,
    RMID_ZCAL,
    RMID_SCAL,
    RMID_COUNT,
    RMID_BINVALID=0xFF,
    RMID_INVALID=0xFFFF
}licor_rmeas_id;

/// @enum licor_mmeas_id
/// @brief measurement meas ID
typedef enum{
    MMID_NONE=0,
    MMID_AIR,
    MMID_STD,
    MMID_ZERO,
    MMID_PRES,
    MMID_POSTS,
    MMID_PREZ,
    MMID_POSTZ,
    MMID_PREB,
    MMID_POSTB,
    MMID_BLINE,
    MMID_SAMPLE,
    MMID_ZCAL,
    MMID_SCAL,
    MMID_COUNT,
    MMID_INVALID=0xFFFF
}licor_mmeas_id;

/// @typedef struct licor_id_map_element_s licor_id_map_element_t
/// @brief licor name/ID map element (key/value); supports ID lookup
/// using UI name tokens 
typedef struct licor_id_map_element_s{
    /// @var licor_id_map_element_t::key
    /// @brief name (lookup key)
    const char *key;
    /// @var licor_id_map_element_t::id
    /// @brief ID value
    int id;
}licor_id_map_element_t;

/// @typedef struct licor_meas_def_s licor_meas_def_t
/// @brief licor measurement profile definition.
typedef struct licor_meas_def_s{
    /// @var licor_meas_def_t::key
    /// @brief name (lookup key)
    const char *key;
    /// @var licor_meas_def_t::mmid
    /// @brief measurement ID
    uint16_t mmid;
    /// @var licor_meas_def_t::blank_ms
    /// @brief blanking interval (msec)
    uint32_t blank_ms;
    /// @var licor_meas_def_t::meas_n
    /// @brief number of licor samples
    uint32_t meas_n;
    /// @var licor_meas_def_t::period_s
    /// @brief licor period (==0.0 or >=0.5)
    float period_s;
    /// @var licor_meas_def_t::cal_wr
    /// @brief calibration write to licor (for zcal, scal)
    uint16_t cal_wr;
}licor_meas_def_t;

/// @enum licor_zrec_field_id
/// @brief licor record field indices.
/// Use caution when changing these, as
/// other items are keyed using their order:
/// g_field_names, licor_oflag_t
typedef enum{
    ZID_CO2=0,
    ZID_CO2ABS,
    ZID_H2O,
    ZID_H2ODEWPOINT,
    ZID_H2OABS,
    ZID_CELLTEMP,
    ZID_CELLPRES,
    ZID_IVOLT,
    ZID_FLOWRATE,
    ZID_RAW_CO2,
    ZID_RAW_CO2REF,
    ZID_RAW_H2O,
    ZID_RAW_H2OREF,
    ZID_FIELD_COUNT
}licor_zrec_field_id;

/// @enum licor_zrec_extra_id
/// @brief licor zrecord extra field indices.
/// Use caution when changing these, as
/// other items are keyed using their order:
/// g_field_names, licor_oflag_t
typedef enum{
    ZID_CO2_EP0=0,
    ZID_CO2_EP1,
    ZID_CO2_AREA,
    ZID_MX,
    ZID_SX,
    ZID_SXY,
    ZID_EXTRA_COUNT
}licor_zrec_extra_id;

/// @def ZID_LICOR_FIELDS
/// @brief number of licor output field indices
#define ZID_LICOR_FIELDS  (ZID_FIELD_COUNT)

/// @def ZID_EXTRA_FIELDS
/// @brief number of licor extra field indices
#define ZID_EXTRA_FIELDS  (ZID_EXTRA_COUNT)

/// @def LI_ZREC_SYNC
/// @brief Licor zrecord sync pattern
/// other options: 0xC02A75EA, 0x0CEA4C02, 0xDEADBEEF
#define LI_ZREC_SYNC 0x0CEA4C02

/// @def LI_ZREC_HDR_LEN
/// @brief length of Licor zrecord header (bytes)
#define LI_ZREC_HDR_LEN      30

/// @def LI_ZREC_SYNC_LEN
/// @brief length of Licor zrecord sync field (bytes)
#define LI_ZREC_SYNC_LEN     4

/// @def LI_ZREC_MDATA_LEN
/// @brief length of Licor measurement data (bytes)
#define LI_ZREC_MDATA_LEN    256

/// @def LI_ZREC_SIZE
/// @brief size of Licor zrecord (bytes)
/// @param pz pointer to licor zrecord
/// @return record size (size_t) on success or 0 otherwise
#define LI_ZREC_SIZE(pz)     ( pz!=NULL ? (size_t)(LI_ZREC_HDR_LEN + pz->hdr.meas_count*LI_ZREC_MDATA_LEN) : (size_t)0 )

/// @def LI_ZREC_SIZEN
/// @brief size of Licor zrecord with given number of measurements(bytes).
///        used to allocate a new zrecord with N measurements
/// @param n number of measurements
/// @return record size (size_t) on success or 0 otherwise
#define LI_ZREC_SIZEN(n)     ( n>=0 ? (size_t)(LI_ZREC_HDR_LEN + n*LI_ZREC_MDATA_LEN) : (size_t)0 )

/// @def LI_ZREC_PMDATA
/// @brief get pointer to zrecord mdata.
/// @param pz pointer to licor zrecord
/// @return pointer to record mdata (mdata_Z_t *) on success or NULL otherwise
#define LI_ZREC_PMDATA(pz)   ( pz!=NULL ? (mdata_Z_t *)((byte *)pz+LI_ZREC_HDR_LEN) : NULL )

/// @def LI_ZREC_DATA_LEN
/// @brief get data length of licor zrecord.
/// @param pz pointer to licor zrecord
/// @return length of record mdata (size_t) on success or NULL otherwise
#define LI_ZREC_DATA_LEN(pz) ( pz!=NULL ? (size_t)(pz->hdr.meas_count*LI_ZREC_MDATA_LEN) : (size_t)0 )

/// @def LI_OREC_SYNC
/// @brief Licor orecord sync pattern
/// other options: 0xC02A75EA, 0x0CEA4C02, 0xDEADBEEF
#define LI_OREC_SYNC 0xC02A75EA

/// @def LI_OREC_SYNC_LEN
/// @brief length of Licor orecord sync field (bytes)
#define LI_OREC_SYNC_LEN     4

#define LI_OREC_SIZE sizeof(licor_orecord_t)

/// @enum queue_odev_flag
/// @brief output device flags.
typedef enum{
    QOF_NONE=      0x00000000,
    QOF_QUEUE=     0x00000001,
    QOF_CONSOLE=   0x00000002,
    QOF_HOST=      0x00000004,
    QOF_LOG=       0x00000008,
    QOF_LOG_RAW=   0x00000010,
    QOF_LOG_OREC=  0x00000020,
    QOF_DEV_RES2=  0x00000040,
    QOF_DEV_RES3=  0x00000080,
           
    QOF_PRETTY=    0x00000100,
    QOF_RAW=       0x00000200,
    QOF_BASE85=    0x00000400,
    QOF_UUENCODE=  0x00000800,
    QOF_HEX=       0x00001000,
    QOF_PRETTY_HEX=0x00002000,
    QOF_CSV=       0x00004000,
    QOF_CSVX=      0x00008000,
            
    QOF_MLAST=     0x00010000,
    QOF_MIDX=      0x00020000,
    QOF_RECORD=    0x00040000,
    QOF_SRC_RES0=  0x00080000,
    QOF_SRC_RES1=  0x00100000,
    QOF_SRC_RES2=  0x00200000,
    QOF_SRC_RES3=  0x00400000,
    QOF_SRC_RES4=  0x00800000,
            
    QOF_HEADER=    0x01000000,
    QOF_DATA=      0x02000000,
    QOF_VERBOSE=   0x04000000,
    QOF_CON_RES0=  0x08000000,
    QOF_CON_RES1=  0x10000000,
    QOF_CON_RES2=  0x20000000,
    QOF_CON_RES3=  0x40000000,
    QOF_CON_RES4=  0x80000000,
            
}queue_output_flag;

/// @def QOF_DEV_FLAGS
/// @brief Queue Output Flag: device option mask
#define QOF_DEV_FLAGS     0x000000FF
/// @def QOF_FMT_FLAGS
/// @brief Queue Output Flag: format option mask
#define QOF_FMT_FLAGS     0x0000FF00
/// @def QOF_SRC_FLAGS
/// @brief Queue Output Flag: source option mask
#define QOF_SRC_FLAGS     0x00FF0000
/// @def QOF_CONTENT_FLAGS
/// @brief Queue Output Flag: content option mask
#define QOF_CONTENT_FLAGS 0xFF000000

#ifdef WITH_LOG_UTILS
/// @enum srch_type_t
/// @brief log search IDs (used by lless)
typedef enum {
    SRCH_NONE,
    SRCH_TIME,
    SRCH_RTID,
    SRCH_RMID,
    SRCH_MMID,
    SRCH_IDX
}srch_type_t;

/// @typedef struct log_search_s log_search_t
/// @brief log search parameters (used by lless)
typedef struct log_search_s{
    /// @var log_search_t::type
    /// @brief search type ID
    srch_type_t type;
    /// @var log_search_t::fwd
    /// @brief search direction (fwd if true)
    bool fwd;
    /// @var log_search_t::union
    /// @brief search target values
    union{
        uint16_t u16val;
        uint32_t u32val;
        uint64_t u64val;
    };
}log_search_t;
#endif //WITH_LOG_UTILS

/// @enum licor_mprec_dfield_id 
/// @brief licor record field indices.
/// Use caution when changing these, as 
/// other items are keyed using their order:
/// g_field_names, licor_oflag_t
typedef enum{
    FID_CO2=0,
    FID_CO2ABS,
    FID_H2O,
    FID_H2ODEWPOINT,
    FID_H2OABS,
    FID_CELLTEMP,
    FID_CELLPRES,
    FID_IVOLT,
    FID_FLOWRATE,
    FID_RAW_CO2,
    FID_RAW_CO2REF,
    FID_RAW_H2O,
    FID_RAW_H2OREF,
    FID_PEAK,
    FID_AREA,        
    FID_FIELD_COUNT
}licor_mprec_dfield_id;

/// @def FID_LICOR_FIELDS
/// @brief number of licor output field indices
/// [FID_CO2-FID_RAW_H2OREF]
#define FID_LICOR_FIELDS  (FID_RAW_H2OREF+1)

/// @enum licor_oflag_t 
/// @brief licor output flags.
/// Indicates which fields should be included in output records.
/// The data flags must be first and 
/// must be in same order as the 
/// field ID flags
typedef enum{
    LOF_RS232_CO2        = (1<<FID_CO2),
    LOF_RS232_CO2ABS     = (1<<FID_CO2ABS),
    LOF_RS232_H2O        = (1<<FID_H2O),
    LOF_RS232_H2ODEWPOINT= (1<<FID_H2ODEWPOINT), 
    LOF_RS232_H2OABS     = (1<<FID_H2OABS),
    LOF_RS232_CELLTEMP   = (1<<FID_CELLTEMP),
    LOF_RS232_CELLPRES   = (1<<FID_CELLPRES),
    LOF_RS232_IVOLT      = (1<<FID_IVOLT),
    LOF_RS232_FLOWRATE   = (1<<FID_FLOWRATE),
    LOF_RAW_CO2          = (1<<FID_RAW_CO2),
    LOF_RAW_CO2REF       = (1<<FID_RAW_CO2REF),
    LOF_RAW_H2O          = (1<<FID_RAW_H2O),
    LOF_RAW_H2OREF       = (1<<FID_RAW_H2OREF),
    LOF_PEAK             = (1<<FID_PEAK),
    LOF_AREA             = (1<<FID_AREA),
    LOF_SAMPLE_N         =0x00008000,
    LOF_REC_TYPE         =0x00010000,
    LOF_REC_CTX          =0x00020000,
    LOF_OFLAGS           =0x00040000,
    LOF_SEQ_N            =0x00080000,
    LOF_TS_ISO8601       =0x00100000,
    LOF_TS_POSIX         =0x00200000
}licor_oflag_t;   

/// @def LICOR_VER_DFL
/// @brief default licor firmware version number 
#define LICOR_VER_DFL "-"
/// @def LICOR_SN_DFL
/// @brief default licor serial number 
#define LICOR_SN_DFL "-"
/// @def LICOR_OUTRATE_DFL
/// @brief default licor output rate 
#define LICOR_OUTRATE_DFL 0
/// @def LICOR_FILTER_DFL
/// @brief default licor filter value
#define LICOR_FILTER_DFL  8
/// @def LICOR_VTYPE_DFL
/// @brief default valve type
#define LICOR_VTYPE_DFL  VTYPE_MONO
/// @def LICOR_REC_DEL_DFL
/// @brief default licor delimiter
#define LICOR_REC_DEL_DFL ","
/// @def LICOR_REC_SEP_DFL
/// @brief default licor record separator
#define LICOR_REC_SEP_DFL "\r\n"
/// @def LICOR_FLOAT_ERRVAL_DFL
/// @brief default licor error value
#define LICOR_FLOAT_ERRVAL_DFL ((float)0.0)

/// @def LI_CFLAG_DFL
/// @brief LI-COR configuration flag default
#define LI_CFLAG_DFL ( LCF_RS232_CO2 | LCF_RS232_CO2ABS | \
LCF_RS232_H2O | LCF_RS232_H2ODEWPOINT |  LCF_RS232_H2OABS | \
LCF_RS232_CELLTEMP | LCF_RS232_CELLPRES |  \
LCF_RS232_IVOLT | LCF_RS232_FLOWRATE | \
LCF_RS232_RAW)

/// @def LI_OFLAG_DFL
/// @brief LI-COR record output flag default
#define LI_OFLAG_DFL ( LOF_TS_ISO8601 | LOF_REC_TYPE | LOF_REC_CTX | LOF_OFLAGS | LOF_SEQ_N | \
LOF_RS232_CO2 | LOF_RS232_CO2ABS | \
LOF_RS232_H2O | LOF_RS232_H2ODEWPOINT | LOF_RS232_H2OABS | \
LOF_RS232_CELLTEMP | LOF_RS232_CELLPRES | \
LOF_RS232_IVOLT | LOF_RS232_FLOWRATE | \
LOF_RAW_CO2 | LOF_RAW_CO2REF |LOF_RAW_H2O |LOF_RAW_H2OREF | \
LOF_AREA| LOF_PEAK  )
//LOF_RAW_CO2 | LOF_RAW_CO2REF |LOF_RAW_H2O |LOF_RAW_H2OREF |

/// @def LI_CFLAG_MNEM_BYTES
/// @brief max cflag mnemonic length (bytes)
#define LI_CFLAG_MNEM_BYTES 40
/// @def LI_CFLAG_MNEM_FIELDS
/// @brief max cflag mnemonic fields
#define LI_CFLAG_MNEM_FIELDS 15
/// @def LI_OFLAG_MNEM_BYTES
/// @brief max oflag mnemonic length (bytes)
#define LI_OFLAG_MNEM_BYTES 64
/// @def LI_OFLAG_MNEM_FIELDS
/// @brief max oflag mnemonic fields
#define LI_OFLAG_MNEM_FIELDS 22

/// @enum licor_cflag_t 
/// @brief licor configuration flags.
typedef enum{
    LCF_RS232_CO2        =0x0001,
    LCF_RS232_CO2ABS     =0x0002,
    LCF_RS232_H2O        =0x0004,
    LCF_RS232_H2ODEWPOINT=0x0008,
    LCF_RS232_H2OABS     =0x0010,
    LCF_RS232_CELLTEMP   =0x0020,
    LCF_RS232_CELLPRES   =0x0040,
    LCF_RS232_IVOLT      =0x0080,
    LCF_RS232_FLOWRATE   =0x0100,
    LCF_RS232_RAW        =0x0200,
    LCF_RS232_STRIP      =0x0400,
    LCF_RS232_ECHO       =0x0800,
    LCF_CFG_HEATER       =0x1000,
    LCF_CFG_ALARM        =0x2000,
    LCF_CFG_PUMP_EN      =0x4000
}licor_cflag_t; 

/// @typedef enum licor_mbuf_handle_t
/// @brief measurement buffer handles (indices)
typedef enum{
    LI_MBUF_HND_INVALID=-1,
    LI_MBUF_CYCLE=0,
    LI_MBUF_XFER,
    LI_MBUF_COUNT
}licor_mbuf_handle_t;

//typedef enum{
//    LSF_LOG_RAW=0x0001,
//    LSF_LOG_ZREC=0x0002,
//    LSF_LOG_OREC=0x0004
//}licor_sflag_t;

/// @typedef struct licor_config_s licor_config_t
/// @brief licor configuration definition.
typedef struct licor_config_s
{
    /// @var licor_config_s::model
    /// @brief licor model number
    int16_t model;
    /// @var licor_config_s::version
    /// @brief licor firmware version [x.y.z]
    char version[12];
    /// @var licor_config_s::serial
    /// @brief licor serial number [e.g. hga-5044]
    char serial[12];
    /// @var licor_config_s::cflags
    /// @brief configuration flags
    licor_cflag_t cflags;
    /// @var licor_config_s::oflags
    /// @brief output flags
    licor_oflag_t oflags;
    /// @var licor_config_s::outrate
    /// @brief output rate
    float outrate;
    /// @var licor_config_s::filter
    /// @brief filter value
    uint16_t filter;
    /// @var licor_config_s::rec_del
    /// @brief record field delimiter
    const char *rec_del;
    /// @var licor_config_s::rec_sep
    /// @brief record separator
    const char *rec_sep;
}licor_config_t;

#pragma pack(push,1)

/// @typedef struct licor_orecord_s licor_orecord_t
/// @brief licor value record type
typedef struct licor_orecord_s 
{
    uint32_t sync;
    /// @var licor_orecord_s::timestamp
    /// @brief timestamp
    uint64_t timestamp;
    /// @var licor_orecord_s::id
    /// @brief id, measurement context (MSB:rid LSB:mid)
    uint16_t id;
    /// @var licor_orecord_s::seq_n
    /// @brief cycle sequence number
    uint32_t seq_n;
    /// @var licor_orecord_s::cycle_id
    /// @brief cycle ID
    uint32_t cycle_id;
    /// @var licor_orecord_s::meas_n
    /// @brief measurement sequence number
    uint16_t meas_n;
    /// @var licor_orecord_s::ens_n
    /// @brief ensemble sequence number
    uint16_t ens_n;
    /// @var licor_orecord_s::ens_len
    /// @brief ensemble length (observations in ensemble)
    uint16_t ens_len;
    /// @var licor_orecord_s::period_s
    /// @brief nominal observation period (sec)
    float period_s;
    /// @var licor_orecord_s::fields
    /// @brief data fields [indexed using FID_... enum]
    float fields[FID_LICOR_FIELDS];
}licor_orecord_t; 

typedef struct licor_orecord_ctx_s 
{
    /// @var licor_orecord_ctx_s::meas_prof
    /// @brief measurement profile (metadata)
    meas_profile_t *meas_prof;
    uint32_t cycle_id;
    uint16_t meas_seq;
    uint16_t ensemble_seq;
    uint32_t flags;
}licor_orecord_ctx_t; 
#pragma pack(pop)

#pragma pack(push,1)

/// @typedef struct log_rec_header_s log_rec_header_t
/// @brief log record header
typedef struct log_rec_header_s
{
    /// @var log_rec_header_s::sync
    /// @brief sync pattern
    uint32_t sync;
    /// @var log_rec_header_s::type_id
    /// @brief record type (structure)
    uint16_t type_id;
    /// @var log_rec_header_s::meas_id
    /// @brief record context.
    uint16_t meas_id;
    /// @var log_rec_header_s::timestamp
    /// @brief timestamp
    uint64_t timestamp;
    /// @var log_rec_header_s::seq_n
    /// @brief record sequence number
    uint32_t seq_n;
    /// @var log_rec_header_s::cycle_id
    /// @brief cycle ID
    uint32_t cycle_id;
    /// @var log_rec_header_s::meas_count
    /// @brief mdata array len
    int16_t meas_count;
    /// @var log_rec_header_s::len
    /// @brief record len (bytes)
    uint32_t len;
}log_rec_header_t;

/// @typedef struct mdata_Z_s mdata_Z_t
/// @brief sample record
typedef struct mdata_Z_s{
    /// @var mdata_Z_s::timestamps
    /// @brief beginning and end timestamps
    uint64_t timestamps[2];
    /// @var mdata_S_s::meas_id
    /// @brief measurement type
    uint16_t meas_id;
    /// @var mdata_S_s::stat_n
    /// @brief sample size (N measurements)
    uint16_t stat_n;
    /// @var mdata_S_s::period
    /// @brief licor output period
    float period;
    /// @var mdata_X_s::M
    /// @brief mean values
    float M[ZID_LICOR_FIELDS];
    /// @var mdata_X_s::S
    /// @brief standard deviation values
    float S[ZID_LICOR_FIELDS];
    /// @var mdata_X_s::min
    /// @brief max values
    float min[ZID_LICOR_FIELDS];
    /// @var mdata_X_s::max
    /// @brief min values
    float max[ZID_LICOR_FIELDS];
    /// @var mdata_X_s::extra
    /// @brief extra (non-licor) parameters
    float X[ZID_EXTRA_FIELDS];
}mdata_Z_t;

/// @typedef struct licor_zrecord_s licor_zrecord_t
/// @brief sample log record
typedef struct licor_zrecord_s
{
    /// @var licor_zrecord_s::log_rec_header_t
    /// @brief record header
    log_rec_header_t hdr;
    /// @var licor_zrecord_s::data
    /// @brief record data
    /// zrecords are allocated so that the data
    /// follows the header.
}licor_zrecord_t;

typedef struct licor_zctx_s{
    licor_mbuf_handle_t mbuf_hnd;
    licor_orecord_ctx_t *octx;
}licor_zctx_t;

#pragma pack(pop)

/////////////////////////
// Exports
/////////////////////////

#ifdef __cplusplus
extern "C" {
#endif
    extern licor_config_t g_licor_config;

    /// @fn licor_rtype_id licor_rtid_slookup(const char *key)
    /// @brief lookup record type ID by name
    /// @param key lookup key (name)
    /// @return ID on success, RTID_INVALID otherwise
    licor_rtype_id licor_rtid_slookup(const char *key);
    /// @fn licor_rmeas_id licor_rmid_slookup(const char *key)
    /// @brief lookup record measurement type ID by name
    /// @param key lookup key (name)
    /// @return ID on success, RMID_INVALID otherwise
    licor_rmeas_id licor_rmid_slookup(const char *key);
    /// @fn licor_mmeas_id licor_mmid_slookup(const char *key)
    /// @brief lookup measurement type ID by name
    /// @param key lookup key (name)
    /// @return ID on success, MMID_INVALID otherwise
    licor_mmeas_id licor_mmid_slookup(const char *key);
    /// @fn const licor_meas_def_t *licor_meas_slookup(const char *key)
    /// @brief lookup measurement profile by name
    /// @param key lookup key (name)
    /// @return pointer to measurement profile on success, NULL otherwise
    const licor_meas_def_t *licor_meas_slookup(const char *key);
    /// @fn const licor_meas_def_t *licor_meas_ilookup(const char *key)
    /// @brief lookup measurement profile by ID (index))
    /// @param idx lookup key (ID)
    /// @return pointer to measurement profile on success, NULL otherwise
    const licor_meas_def_t *licor_meas_ilookup(int idx);
    
    /// @fn void licor_meas_def_show(const licor_meas_def_t *self, uint16_t indent)
    /// @brief output measurement definition to console
    /// @param self pointer to measurement profile definition
    /// @param indent space to indent output
    /// @return none
    void licor_meas_def_show(const licor_meas_def_t *self, uint16_t indent);

    /// @fn int licor_logrechdr_show(log_rec_header_t *self, bool verbose, int indent)
    /// @brief output log_rec_header_t contents to console
    /// @param self pointer to instance
    /// @param verbose use verbose output, where applicable
    /// @param indent space to indent output
    /// @return number of bytes output on success, 0 otherwise
    int licor_logrechdr_show(log_rec_header_t *self, bool verbose, int indent);

    /// @fn int licor_zrecord_show(licor_zrecord_t *self, bool verbose, int indent)
    /// @brief output licor_zrecord_t contents to console
    /// @param self pointer to instance
    /// @param verbose use verbose output, where applicable
    /// @param indent space to indent output
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_show(licor_zrecord_t *self, bool verbose, int indent);
    
    /// @fn int licor_zrecord_outr(licor_zrecord_t *self, char *term)
    /// @brief output licor_zrecord_t contents to console (raw bytes)
    /// @param self pointer to instance
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_outr(licor_zrecord_t *self, char *term);

    /// @fn int licor_zrecord_outb(licor_zrecord_t *self, char *term)
    /// @brief output licor_zrecord_t contents to console (z85/base-64 encoded)
    /// @param self pointer to instance
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_outb(licor_zrecord_t *self, char *term);

    /// @fn int licor_zrecord_outc(licor_zrecord_t *self, char *term)
    /// @brief output licor_zrecord_t contents to console (CSV)
    /// @param self pointer to instance
    /// @param del record field delimiter string (required, non-NULL)
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_outc(licor_zrecord_t *self, queue_output_flag oflags, char *del, char *term);

    /// @fn int licor_zrecord_outc(licor_zrecord_t *self, char *term)
    /// @brief output licor_zrecord_t contents to console (hex, formatted hex)
    /// @param self pointer to instance
    /// @param flags formatting/output flags
    /// @param indent space to indent output
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_outx(licor_zrecord_t *self, sys_hexfmt_flag flags, int indent, char *term);

    //    int licor_zrecord_outu(licor_zrecord_t *self, char *term);
    
    /// @fn int licor_zrecord_outs(licor_zrecord_t *self, char *term)
    /// @brief output licor_zrecord_t contents to console (one or more formats)
    /// @param self pointer to instance
    /// @param oflags formatting/output flags
    /// @return number of bytes output on success, 0 otherwise
    int licor_zrecord_outs(licor_zrecord_t *self, queue_output_flag oflags);

    /// @fn int licor_zmdata_outc(mdata_Z_t *self, char *del, char *term)
    /// @brief output mdata_Z_t contents to console (CSV)
    /// @param self pointer to instance
    /// @param del record field delimiter string (required, non-NULL)
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zmdata_outc(mdata_Z_t *self, queue_output_flag oflags, char *del, char *term);

    /// @fn int licor_zmdata_outc(mdata_Z_t *self, char *del, char *term)
    /// @brief output mdata_Z_t contents to console (CSV)
    /// @param self pointer to instance
    /// @param del record field delimiter string (required, non-NULL)
    /// @param term record terminator (NULL for none) 
    /// @return number of bytes output on success, 0 otherwise
    int licor_zmdata_show(mdata_Z_t *self, bool verbose, int indent);

    /// @fn return_code_t licor_orecord_show(licor_orecord_t *self, bool verbose, int indent)
/// @brief show orecord summary (send to console)
/// @param self record to show
/// @param verbose enable verbose output if true
/// @param indent indent output (spaces)
/// @return none
    int licor_orecord_show(licor_orecord_t *self, bool verbose, int indent);

/// @fn int licor_orecord_str(char *dest, licor_orecord_t *src)
/// @brief value record to string
/// @param dest destination buffer (must be at least LI_VRECORD_STR_BYTES))
/// @param src value record
/// @return number of bytes written on success, -1 otherwise
//    int licor_orecord_str(char *dest, licor_orecord_t *src);
    int licor_orecord_str(char *dest, licor_orecord_t *src, queue_output_flag oflags);

    int licor_orecord_outs(licor_orecord_t *self, queue_output_flag oflags);
    int licor_orecord_outb(licor_orecord_t *self, char *term);
    int licor_orecord_outc(licor_orecord_t *self, queue_output_flag oflags, char *del, char *term );
    int licor_orecord_outr(licor_orecord_t *self, char *term);
    int licor_orecord_outx(licor_orecord_t *self, sys_hexfmt_flag flags, int indent, char *term);

    /// @fn licor_orecord_t *licor_parse_raw(licor_orecord_t *dest, char *src, uint64_t tstamp
    /// @brief parse raw Licor output into licor_orecord_t structure
    /// @param dest pointer to instance
    /// @param src raw data source buffer
    /// @param tstamp timestamp (epoch msec)
    /// @return pointer to parsed record on success, NULL otherwise
    licor_orecord_t *licor_parse_raw(licor_orecord_t *dest, char *src, uint64_t tstamp);
    
    licor_zrecord_t *licor_parse_csv(licor_zrecord_t *dest, size_t len, char *src);

#ifdef __cplusplus
}
#endif

#endif // THIS_FILE



