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

/// 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_H
#define LICOR8XX_H

/////////////////////////
// Includes
/////////////////////////
#include <stdbool.h>
#include <math.h>

#include "cwgsys.h"
#include "cwg-shared.h"
#include "licor8xx-shared.h"
#include "rtc.h"
#include "log.h"

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

/// @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

/// @def LI_UART
/// @brief serial port handle for UART
#define LI_UART SER_LICOR //SER_LICOR //SER_CONS
/// @def LI_WRITE(...))
/// @brief write (printf formatted) data to licor
#define LI_WRITE(...) serPrintf(LI_UART,__VA_ARGS__)
/// @def LI_ACK_RETRIES
/// @brief number of times to retry licor ACK response
#define LI_ACK_RETRIES 3
/// @def LI_ACK_TO_MSEC
/// @brief number of msec to wait for licor ACK response
#define LI_ACK_TO_MSEC   2000
/// @def LI_READ_TO_MSEC
/// @brief licor read timeout msec
#define LI_READ_TO_MSEC 2000
/// @def LI_RECORD_STR_BYTES
/// @brief licor raw record length (buffer size)
#define LI_RAW_RECORD_STR_BYTES 375 

/// @def LI_VRECORD_STR_BYTES
/// @brief value record length (buffer size)
//#define LI_VRECORD_STR_BYTES 180

/// @def LI_ORECORD_STR_BYTES
/// @brief observation record length (buffer size)
#define LI_ORECORD_STR_BYTES 180
/// @def LI_BRECORD_STR_BYTES
/// @brief baseline record length (buffer size)
#define LI_BRECORD_STR_BYTES 128
/// @def LI_CAL_DELAY
/// @brief internal (licor) delay during which
/// output stops while calibration is applied
#define LI_CAL_DELAY_MSEC 10000 

/// @def LI_FRECORD_STR_BYTES
/// @brief feature record length (buffer size)
//#define LI_FRECORD_STR_BYTES 64

/// @def LI_XRECORD_STR_BYTES
/// @brief xrecord record length (buffer size)
#define LI_XRECORD_STR_BYTES 256

/// @def LI_ORATE_ACK_BYTES
/// @brief output rate ACK length (buffer size)
#define LI_ORATE_ACK_BYTES 64
/// @def LI_CFG_QUERY_BYTES
/// @brief configuration request reply length (buffer size)
#define LI_CFG_QUERY_BYTES 416

/// @def LICOR_MODEL_STR(model)
/// @brief licor model number string
/// @param model model number
#define LICOR_MODEL_STR(model) "li"STR(model)
/// @def CFLAG2BSTR(pc,mask)
/// @brief create config flag to binary tag value string
/// @param pc pointer to configuration
/// @param mask config flag mask ()
/// @return "true" if any flags in mask are set, "false" otherwise
#define CFLAG2BSTR(pc,mask) (GET_FLAGS(pc->cflags,mask)?"true":"false")  
/// @def STAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%s</tag>"
#define STAG_FMT(tag)  "<"tag">%s</"tag">"
/// @def ITAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%d</tag>"
#define ITAG_FMT(tag)  "<"tag">%d</"tag">"
/// @def FTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%f</tag>"
#define FTAG_FMT(tag)  "<"tag">%f</"tag">"
/// @def DTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%lf</tag>"
#define DTAG_FMT(tag)  "<"tag">%lf</"tag">"
/// @def ETAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%e</tag>"
#define ETAG_FMT(tag)  "<"tag">%e</"tag">"
/// @def GTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%g</tag>"
#define GTAG_FMT(tag)  "<"tag">%g</"tag">"
/// @def QTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>?</tag>"
#define QTAG_FMT(tag)  "<"tag">?</"tag">"
/// @def YMD_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>%04d-%02d-%02d</tag>"
#define YMD_FMT(tag)  "<"tag">%04d-%02d-%02d</"tag">"
/// @def OTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "<tag>"
#define OTAG_FMT(tag)  "<"tag">"
/// @def CTAG_FMT(tag)
/// @brief create XML tag printf format string
/// @param tag tag name 
/// @return "</tag>"
#define CTAG_FMT(tag)  "</"tag">"
/// @def LI_WR_LOTAG(model)
/// @brief write XML tag 
/// @param model licor model number
#define LI_WR_LOTAG(model) LI_WRITE("<li%d>",model)
/// @def LI_WR_LCTAG(model)
/// @brief write XML tag 
/// @param model licor model number
#define LI_WR_LCTAG(model) LI_WRITE("</li%d>",model)
/// @def LI_WR_BTAG(pc,tag,f)
/// @brief write XML tag 
/// @param pc configuration pointer
/// @param tag tag name
/// @param f flag (mask)
#define LI_WR_BTAG(pc,tag,f) LI_WRITE(STAG_FMT(tag),FLAG2BSTR(pc,f))
/// @def LI_WR_ITAG(pc,tag,f)
/// @brief write XML tag 
/// @param tag tag name
/// @param ival integer value
#define LI_WR_ITAG(tag,ival) LI_WRITE(ITAG_FMT(tag),ival)
/// @def LI_WR_FTAG(pc,tag,f)
/// @brief write XML tag 
/// @param tag tag name
/// @param fval float value
#define LI_WR_FTAG(tag,fval) LI_WRITE(FTAG_FMT(tag),fval)
/// @def LI_WR_DTAG(pc,tag,f)
/// @brief write XML tag 
/// @param tag tag name
/// @param dval double value
#define LI_WR_DTAG(tag,dval) LI_WRITE(DTAG_FMT(tag),dval)
/// @def LI_WR_ETAG(pc,tag,f)
/// @brief write XML tag 
/// @param tag tag name
/// @param dval double value
#define LI_WR_ETAG(tag,dval) LI_WRITE(ETAG_FMT(tag),dval)
/// @def LI_WR_GTAG(pc,tag,f)
/// @brief write XML tag 
/// @param tag tag name
/// @param dval double value
#define LI_WR_GTAG(tag,dval) LI_WRITE(GTAG_FMT(tag),dval)
/// @def LI_WR_DATETAG(pc,tag,f)
/// @brief write XML tag 
/// @param y year (yyyy)
/// @param m month 1:12
/// @param d day 1:31
#define LI_WR_DATETAG(y,m,d) LI_WRITE(YMD_FMT("date"),y,m,d)

/// @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_PUB_PREV
/// @brief publishing destination [data queue]
#define LI_PUB_QUEUE "Q"
/// @def LI_PUB_CONS
/// @brief publishing destination [console port]
#define LI_PUB_CONS "C"
/// @def LI_PUB_LOG
/// @brief publishing destination [log]
#define LI_PUB_LOG  "L"
/// @def LI_PUB_HOST
/// @brief publishing destination [host port]
#define LI_PUB_HOST "H"
/// @def LI_PUB_ALL
/// @brief publishing destination [all]
#define LI_PUB_ALL  "A"

/// @def LI_MBUF_CYCLE_DEPTH
/// @brief LI-COR measurement buffer depth (measurements per cycle)
#define LI_MBUF_CYCLE_DEPTH LICOR_MAX_CYCLE_MEASUREMENTS
/// @def LI_MBUF_CYCLE_BYTES
/// @brief cycle buffer size (bytes)
#define LI_MBUF_CYCLE_BYTES (sizeof(z_meas_buffer_t)+LI_MBUF_CYCLE_DEPTH*(sizeof(mdata_Z_t)+sizeof(sdata_Z_t)))
/// @def LI_MBUF_XFER_DEPTH
/// @brief transfer buffer depth (measurements; should be >= cycle buffer)
#define LI_MBUF_XFER_DEPTH LICOR_MAX_CYCLE_MEASUREMENTS
/// @def LI_MBUF_XFER_BYTES
/// @brief transfer buffer size (bytes)
#define LI_MBUF_XFER_BYTES (sizeof(z_meas_buffer_t)+LI_MBUF_XFER_DEPTH*(sizeof(mdata_Z_t)+sizeof(sdata_Z_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 licor_vtype_t
/// @brief valve types
typedef enum{
    VTYPE_MONO='M', 
    VTYPE_LATCHING='L'
}licor_vtype_t;

/// @enum licor_cal_id 
/// @brief calibration IDs.
/// used to index name strings
/// change w/ caution
typedef enum{
    LI_CAL_Z=0,
    LI_CAL_S,
    LI_CAL_S2,
    LI_CAL_INVALID,
    LI_CAL_COUNT
}licor_cal_id;

#define CALTYPE_STR(t) ( t>0 && t<LI_CAL_COUNT ? cal_type_strings[t] : "unsupported"  )

/// @typedef struct licor_s licor_t
/// @brief licor instance.
typedef struct licor_s
{
    /// @var licor_s::cfg
    /// @brief configuration
    licor_config_t *cfg;
    /// @var licor_s::valve_type
    /// @brief valve type
    licor_vtype_t valve_type;
}licor_t;

/// @enum nmeas_handler_id 
/// @brief stream measurement handler ID
typedef enum{
    HID_INVALID='?',
//    HID_BLINE='B',
//    HID_AVG='A',
//    HID_REF='R',
    HID_Z='Z',
}nmeas_handler_id;

//#pragma pack(push,1)

/// @typedef struct sdata_Z_s sdata_S_t
/// @brief sample record state data [passed to avg handler]
typedef struct sdata_Z_s{
    uint64_t timestamp_prev;
    /// @var sdata_Z_s:M_prev
    /// @brief mean computation previous mean values
    double M_prev[ZID_LICOR_FIELDS];
    /// @var sdata_Z_s:V_prev
    /// @brief mean computation previous values
    double V_prev[ZID_LICOR_FIELDS];
    /// @var sdata_Z_s:S_prev
    /// @brief mean computation previous sdev values
    double S_prev[ZID_LICOR_FIELDS];
    double MX_prev;
    double SX_prev;
    double SXY_prev;
}sdata_Z_t;


/// @typedef struct z_meas_buffer_s z_meas_buffer_t
/// @brief z measurement buffer state
typedef struct z_meas_buffer_s
{
    int meas_idx;
    /// @var z_meas_buffer_s::depth
    /// @brief mdata array len
    uint16_t depth;
    /// @var z_meas_buffer_s::sdata
    /// @brief state data ()
    sdata_Z_t *sdata;
    /// @var z_meas_buffer_s::record
    /// @brief measurement   
    licor_zrecord_t record;
}z_meas_buffer_t;

//#pragma pack(pop)

/// @typedef int (* li_handler_fn)(licor_orecord_t *rec, void *args)
/// @brief licor stream handler function.
/// pass in current record and args, which should include all state needed
/// for computing over multiple records.
typedef int (* li_handler_fn)(licor_orecord_t *rec, void *args);

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

#ifdef __cplusplus
extern "C" {
#endif
    
extern const char *cal_type_strings[LI_CAL_COUNT];

/// @fn return_code_t licor_initialize(licor_config_t *pconf)
/// @brief intialize licor
/// @param pconf config pointer (uses default config if NULL)
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_initialize(licor_config_t *pconf);
    
/// @fn return_code_t licor_set_outrate(float rate)
/// @brief set output rate
/// @param rate output period (sec, >=0.5)
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_set_outrate(float rate);
    
/// @fn return_code_t licor_set_strip(bool strip)
/// @brief set XML stripping
/// @param strip true to strip XML, false otherwise
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_set_strip(bool strip);
    
/// @fn return_code_t licor_set_valve_type(licor_vtype_t type)
/// @brief set valve type
/// @param type valve type enum
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_set_valve_type(licor_vtype_t type);
    
/// @fn return_code_t licor_set_model(uint16_t model)
/// @brief set LI-COR model number
/// @param model licor_model_id 
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_set_model(licor_model_id model);
    
/// @fn rlicor_model_id licor_model()
/// @brief get LI-COR model number
/// @return licor model number
    licor_model_id licor_model();
    
/// @fn float licor_outrate()
/// @brief get licor output rate
/// @return output rate
    float licor_outrate();
    
/// @fn licor_cal(licor_cal_id type, double spanc)
/// @brief perform licor calibration
/// @param type calibration type
/// @param spanc span calibration value, [used only for span cals]
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_cal(licor_cal_id type, double spanc);
    
/// @fn void licor_status_show(char *self, bool verbose, int indent);
/// @brief show licor summary (send to console)
/// @param self licor status raw XML
/// @param tag  show only contents inside tag if non-NULL
/// @param verbose enable verbose output if true
/// @param indent indent output (spaces)
/// @return none
    void licor_status_show(char *self, const char *tag, bool verbose, int indent);
    
/// @fn int licor_read_status(char *dest, int len);
/// @brief read licor status (raw xml string))
/// @param dest output buffer (ideally ~2048 bytes)
/// @param len buffer length
/// @return number of bytes read on success, -1 otherwise
    int licor_read_status(char *dest, int len);
    
/// @fn int licor_handler_x(licor_orecord_t *cur, void *args)
/// @brief streamx handler function
/// @param cur current licor record
/// @param args handler state
/// @return 0 in success, -1 otherwise
//    int licor_handler_x(licor_orecord_t *cur, void *args);
    int licor_handler_z(licor_orecord_t *cur, void *args);
    int licor_handler_o(licor_orecord_t *cur, void *args);

/// @fn return_code_t licor_stream_x(float orate, uint16_t n, li_handler_fn rec_fn, void *args, licor_record_type_t type, uint32_t tmout_ms)
/// @brief process a series of raw (XML) licor records
/// @param orate output rate
/// @param n number of samples
/// @param rec_fn handler function
/// @param args handler state arguments
/// @param type record type
/// @param tmout_ms timeout (msec)
/// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_stream_x(uint32_t blank_ms, float orate, uint16_t n, li_handler_fn rec_fn, void *args, char *raw_id, uint32_t tmout_ms, queue_output_flag flags);

/// @fn return_code_t licor_poll_raw(char *dest,byte type)
/// @brief poll the licor, return raw data
/// @param dest destination buffer
/// @param len destination length (bytes)
/// @return ERR_SUCCESS on success, error code otherwise
    int licor_poll_raw(char *dest,int len);

/// @fn licor_orecord_t *licor_parse_measurement(licor_orecord_t *dest, char *src, uint64_t tstamp);
/// @brief parse raw measurement into value record
/// @param dest value record
/// @param src raw record
/// @param tstamp timestamp (epoch msec)
/// @param type record type
/// @return value record (dest) on success, NULL otherwise
    licor_orecord_t *licor_parse_measurement(licor_orecord_t *dest, char *src, uint64_t tstamp);
        
    /// alt_parse functions    
   int licor_mnem2cflag(void *cflag, char *key );
   int licor_mnem2oflag(void *cflag, char *key );
   int licor_cflag2mnem(char *dest, void *src, size_t len);
   int licor_oflag2mnem(char *dest, void *src, size_t len);
   
    z_meas_buffer_t *licor_mbuf_instance(licor_mbuf_handle_t hnd);
    void licor_mbuf_next(licor_mbuf_handle_t hnd);
    void licor_mbuf_reset(licor_mbuf_handle_t hnd);
    void licor_mbuf_set_rtid(licor_mbuf_handle_t hnd, uint16_t type_id);
    void licor_mbuf_set_rmid(licor_mbuf_handle_t hnd, uint16_t meas_id);
    void licor_mbuf_set_rtime(licor_mbuf_handle_t hnd, uint64_t timestamp);
    void licor_mbuf_set_mmid(licor_mbuf_handle_t hnd, uint16_t meas_id);
    void licor_mbuf_set_mmidn(licor_mbuf_handle_t hnd, int meas_idx, uint16_t meas_id);
    void licor_mbuf_set_mperiod(licor_mbuf_handle_t hnd, float period);
    void licor_mbuf_set_mperiodn(licor_mbuf_handle_t hnd, int meas_idx, float period);
    uint16_t licor_mbuf_depth(licor_mbuf_handle_t hnd);
    uint16_t licor_mbuf_mcount(licor_mbuf_handle_t hnd);
    mdata_Z_t *licor_mbuf_mdata(licor_mbuf_handle_t hnd, int idx);
    licor_zrecord_t *licor_mbuf_record(licor_mbuf_handle_t hnd);
    size_t licor_mbuf_record_size(licor_mbuf_handle_t hnd);
    int licor_mbuf_show(z_meas_buffer_t * self, bool verbose, int indent);

    return_code_t licor_zrecord_outl(licor_zrecord_t *self, size_t data_sz, log_flags_t lflags);

    /// @fn licor_queue_out(licor_zrecord_t *self, uint16_t oflags)
    /// @brief output record to specified destination using specified format
    /// @param self record data
    /// @param oflags output flags (specifies destination device(s) and format)
    /// @return ERR_SUCCESS on success, error code otherwise
    return_code_t licor_queue_out(licor_zrecord_t *self, queue_output_flag oflags);
    return_code_t licor_mmeas_out(licor_mbuf_handle_t hnd, int16_t meas_idx,queue_output_flag oflags);

#ifdef __cplusplus
}
#endif

#endif // THIS_FILE

