///
/// @file lutil_impl.c
/// @authors k. headley
/// @date 05 dec 2019

/// Log utility API implementations

/// @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 LUTIL_IMPL_H
#define LUTIL_IMPL_H

/////////////////////////
// Includes 
/////////////////////////
#include "cwg-shared.h"
#include "licor8xx-shared.h"

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

/////////////////////////
// Type Definitions
/////////////////////////
#undef WITH_DQUTIL

// use same value for invalid or current position
#define LOG_OFS_INVALID 0xFFFFFFFF

typedef uint32_t log_ofs_t;

typedef enum{
    LU_INVALID=0,
    LU_OBIN,
    LU_ZBIN,
    LU_RAW,
    LU_SYS,
    LU_ODQ,
    LU_ZDQ
}lutil_type_id;

typedef enum{
    OF_NONE=0x0,
    OF_DIR=0x1,
    OF_OFS=0x2,
    OF_NOSYNC=0x4,
    OF_FILTER=0x8
}lutil_opflags_t;


#define DIR_FLG_FWD OF_DIR
#define DIR_FLG_REV 0x0
#define OFS_FLG_ABS OF_OFS
#define OFS_FLG_REL 0x0
#define SYN_FLG_NOSYNC OF_NOSYNC
#define SYN_FLG_SYNC 0x0
#define FILT_FLG_FILTER OF_FILTER
#define FILT_FLG_NOFILTER 0x0

#define DIR_SET_FWD(pf) if(NULL!=pf)*pf |= OF_DIR
#define DIR_SET_REV(pf) if(NULL!=pf)*pf &= ~(OF_DIR)
#define DIR_IS_FWD(f) ( (f&OF_DIR) != 0 ? true : false)
#define DIR_IS_REV(f) ( (f&OF_DIR) == 0 ? true : false)
#define DIR_GET(f) (f&OF_DIR)

#define OFS_SET_ABS(pf) *pf |= OF_OFS
#define OFS_SET_REL(pf) *pf &= ~(OF_OFS)
#define OFS_IS_ABS(f) ( (f&OF_OFS) != 0 ? true : false)
#define OFS_IS_REL(f) ( (f&OF_OFS) == 0 ? true : false)
#define OFS_GET(f) (f&OF_OFS)

#define SYNC_SET_DIS(pf) *pf |= OF_NOSYNC
#define SYNC_SET_EN(pf) *pf &= ~(OF_NOSYNC)
#define SYNC_IS_DIS(f) ( (f&OF_NOSYNC) != 0 ? true : false)
#define SYNC_IS_EN(f) ( (f&OF_NOSYNC) == 0 ? true : false)
#define SYNC_GET(f) (f&OF_NOSYNC)

#define FILT_SET_EN(pf) *pf |= OF_FILTER
#define FILT_SET_DIS(pf) *pf &= ~(OF_FILTER)
#define FILT_IS_EN(f) ( (f&OF_FILTER) != 0 ? true : false)
#define FILT_IS_DIS(f) ( (f&OF_FILTER) == 0 ? true : false)
#define FILT_GET(f) (f&OF_FILTER)

typedef enum{
    CUR_POS=0,
    CUR_FIRST,
    CUR_LAST,
    CUR_EOF,
    CUR_SAVE,
    CUR_COUNT
}lutil_cur_idx;

typedef enum {
    ST_INVALID=0,
    ST_IDX,
    ST_TIME,
    ST_RTYPE,
    ST_RTID,
    ST_RMID,
    ST_MMID,
    ST_XID,
    ST_SEQN,
    ST_CYCN,
    ST_MSN,
    ST_ESN
}lutil_search_id_t;

typedef enum{
    LINF_REC_COUNTER=0x01,
    LINF_CSV_HEADER =0x02,
    LINF_FMT_PRETTY =0x04,
    LINF_FMT_CSV    =0x08,
    LINF_TIME_POSIX =0x10,
    LINF_TIME_ISO   =0x20,
}linfo_flags;
#define LINF_FMT_FLAGS  0x0C
#define LINF_TIME_FLAGS 0x30

typedef enum{
    CTYPE_NONE=0,
    CTYPE_FLOAT,
    CTYPE_DOUBLE,
    CTYPE_I16,
    CTYPE_I32,
    CTYPE_I64,
    CTYPE_U16,
    CTYPE_U32,
    CTYPE_U64
}cmp_type_t;

typedef enum{
    OP_INVALID=0,
    OP_EQ,
    OP_NE,
    OP_GT,
    OP_LT,
    OP_GE,
    OP_LE,
    OP_RI,
    OP_RO,
}cmp_op_id;

typedef union{
    float fval;
    double dval;
    int16_t i16;
    int32_t i32;
    int64_t i64;
    uint16_t u16;
    uint32_t u32;
    uint64_t u64;
}cmp_val_t;

typedef struct log_search_def_s{
    // search expression (implementation-specific)
    lutil_search_id_t type;
    cmp_type_t cmp_type;
    cmp_op_id op_id;
    uint32_t idx;
    cmp_val_t val[2];
}limpl_search_def_t;

typedef struct lutil_opts_s{
    bool last_n;
    uint32_t start;
    uint32_t count;
    queue_output_flag oflags;
    linfo_flags iflags;
    lutil_opflags_t opflags;
    lutil_type_id src_type;
    char *src;
    limpl_search_def_t search;
}lutil_opts_t;

// linspect
typedef enum{
    R_RMID_INVAL=0,
    R_RMID_NONE,
    R_RMID_SAMPLE,
    R_RMID_SCAL,
    R_RMID_ZCAL,
    R_RTID_INVAL,
    R_RTID_ZRECV1,
    R_MMID_NONE,
    R_MMID_AIR,
    R_MMID_STD,
    R_MMID_ZERO,
    R_MMID_PRES,
    R_MMID_POSTS,
    R_MMID_PREZ,
    R_MMID_POSTZ,
    R_MMID_PREB,
    R_MMID_POSTB,
    R_MMID_BLINE,
    R_MMID_SAMPLE,
    R_MMID_ZCAL,
    R_MMID_SCAL,
    RBIN_COUNT
}linfo_recbin_idx;

typedef enum{
    E_SYNC=0,
    E_TMZERO,
    E_TMMONO,
    E_CTINVAL,
    E_SEQMONO,
    E_LENINVAL,
    E_RTID,
    E_RMID,
    E_MMID,
    E_DATA,
    EBIN_COUNT
}linfo_errbin_idx;

typedef enum{
    T_FIRST=0,
    T_LAST,
    T_RNGSTART,
    T_RNGEND,
    TBIN_COUNT
}linfo_timebin_idx;

typedef enum{
    SEQ_FIRST=0,
    SEQ_LAST,
    SEQ_RNGSTART,
    SEQ_RNGEND,
    SQBIN_COUNT
}linfo_seqbin_idx;

typedef struct linfo_stats_s{
    uint32_t fsize;
    uint64_t time_bin[TBIN_COUNT];
    uint32_t seq_bin[SQBIN_COUNT];
    int record_bin[RBIN_COUNT];
    int err_bin[EBIN_COUNT];
    int record_count;
    int error_count;
    uint32_t seq_last;
    uint64_t time_last;
}linfo_stats_t;

// lless navigation struct describes
// record cursor movements
typedef enum{
    NAV_NONE=0,
    NAV_ABS,
    NAV_REL,
    NAV_HEAD,
    NAV_TAIL,
    NAV_COUNT
}lless_nav_type;

typedef struct nav_s{
    lless_nav_type type;
    lutil_opflags_t flags;
    log_ofs_t start;
    uint32_t count;
    bool wrap;
}lless_nav_t;

#ifdef WITH_DQUTIL
typedef enum{
    DQC_INVALID=0,
    DQC_SHOW,
    DQC_ADD,
    DQC_DEL,
    DQC_MOVE,
    DQC_INSPECT
}dqutil_cmd_t;

typedef struct dqutil_opts_s{
    dqutil_cmd_t cmd;
    bool last_n;
    uint32_t start;
    uint32_t count;
    queue_output_flag oflags;
    lutil_opflags_t opflags;
    uint32_t index;
    bool idx_rel;
    lutil_type_id src_type;
    char *src;
    char *dest;
    limpl_search_def_t search;
}dqutil_opts_t;
#endif


// declare struct here, define/typedef later
// to resolve circular definitions
struct log_impl_s;

typedef int (* limpl_sync_fn)(struct log_impl_s *self, lutil_opflags_t flags, log_ofs_t *r_ofs, uint32_t *r_sync);
typedef int (* limpl_traverse_fn)(struct log_impl_s *self, lutil_opflags_t flags, uint32_t count,  log_ofs_t *r_ofs, uint32_t *r_count);
typedef int (* limpl_seek_fn)(struct log_impl_s *self, lutil_opflags_t flags, uint32_t start, uint32_t count, log_ofs_t *r_ofs, uint32_t *r_count);
typedef int (* limpl_read_fn)(struct log_impl_s *self, byte *dest, uint32_t len, uint32_t count, uint32_t *r_count);
typedef int (* limpl_search_fn)(struct log_impl_s *self, lutil_opflags_t flags, limpl_search_def_t *srch_def, log_ofs_t *r_ofs);
typedef int (* limpl_compare_fn)(byte *src, limpl_search_def_t *srch);
typedef int (* limpl_show_fn)(struct log_impl_s *self, int indent, lutil_opts_t *opts);
typedef int (* limpl_bshow_fn)(struct log_impl_s *self, byte *src, int indent, lutil_opts_t *opts);
typedef int (* limpl_nshow_fn)(struct log_impl_s *self, int indent, lutil_opts_t *opts);
typedef int (* limpl_inspect_fn)(struct log_impl_s *self, int indent, lutil_opts_t *opts);

typedef struct log_impl_s{
    char *fpath;
#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s;
    FILE_T *fp;
#else
    FILE_T *fp;
#endif
    uint32_t sync_bytes;
    uint32_t buf_len;
    log_ofs_t cur[CUR_COUNT];
    limpl_sync_fn sync;
    limpl_traverse_fn traverse;
    limpl_seek_fn seek;
    limpl_search_fn search;
    limpl_compare_fn compare;
    limpl_read_fn read;
    limpl_show_fn show;
    limpl_bshow_fn bshow;
    limpl_nshow_fn nshow;
    limpl_inspect_fn inspect;
}log_impl_t;


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

#ifdef __cplusplus
extern "C" {
#endif

    int limpl_init(log_impl_t *self, char *fpath, lutil_type_id type);
    int limpl_head(log_impl_t *self);
    int limpl_tail(log_impl_t *self);
    int limpl_fseek(log_impl_t *self, uint32_t ofs);
    uint32_t limpl_ftell(log_impl_t *self);
    bool limpl_feof(log_impl_t *self);
    bool limpl_ferr(log_impl_t *self);
    int limpl_sync_u32(log_impl_t *self, lutil_opflags_t flags, log_ofs_t *r_ofs, uint32_t *r_sync);
    int limpl_next(log_impl_t *self, log_ofs_t *r_ofs);
    int limpl_seek(log_impl_t *self, lutil_opflags_t flags, uint32_t start, uint32_t count, log_ofs_t *r_ofs, uint32_t *r_count);

    int limpl_nshow(log_impl_t *self, int indent, lutil_opts_t *opts);
    int limpl_search(log_impl_t *self, lutil_opflags_t flags,limpl_search_def_t *srch, log_ofs_t *r_ofs);
    int limpl_search_def_show(limpl_search_def_t *self);

    int limpl_parse_lutil_opts(lutil_opts_t *cfg, int opt_n, char **opts);
    void lutil_show_opts(lutil_opts_t *self);

    int limpl_lless(log_impl_t *impl, lutil_opts_t *opts);
    cmp_op_id limpl_cmp_parse_op(char *expr);

#ifdef WITH_DQUTIL
    int limpl_dqutil(log_impl_t *impl, dqutil_opts_t *opts);
    int limpl_parse_dqutil_opts(dqutil_opts_t *cfg, int opt_n, char **opts);
    void dqutil_show_opts(dqutil_opts_t *self);
#endif //WITH_DQUTIL

#ifdef __cplusplus
}
#endif

#endif // THIS_FILE
