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

/// Log implementation

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

/// TODO: segmentation, search, filters?

/////////////////////////
// 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 LOG_H
#define LOG_H

/////////////////////////
// Includes 
/////////////////////////

#include "cwgsys.h"
#include "cwg-shared.h"
#include "pic24.h"
#include "rtc.h"

/////////////////////////
// Macros
/////////////////////////
/// @def KBYTE
/// @brief Kilobyte
#define KBYTE (1024ul)
/// @def MBYTE
/// @brief Megabyte
#define MBYTE (1024ul*1024ul)
/// @def GBYTE
/// @brief Gigabyte
#define GBYTE (1024ul*1024ul*1024ul)

/// @def LOG_NAME_BYTES
/// @brief log name characters 
/// (names are FATFS 8.3, NNNNDDDD.SSS)
#define LOG_NAME_BYTES 4
/// @def LOG_DATE_BYTES
/// @brief log date characters 
/// (names are FATFS 8.3, NNNNDDDD.SSS)
#define LOG_DATE_BYTES 4
/// @def LOG_SUFFIX_BYTES
/// @brief log date characters 
/// (names are FATFS 8.3, NNNNDDDD.SSS)
#define LOG_SUFFIX_BYTES 3
/// @def LOG_PATH_BYTES
/// @brief log path characters 
/// (names are FATFS 8.3+NULL, NNNNDDDD.SSS)
#define LOG_PATH_BYTES 13
/// @def LOG_TIMESTAMP_BYTES
/// @brief log timestamp characters 
// ISO8601 : YYYY/MM/DDThh:mm:ssZ+NULL (>=21 bytes)
#define LOG_TIMESTAMP_BYTES 24
/// @def LOG_SYSLOG_MSG_BYTES
/// @brief syslog message len (fallback value)
#define LOG_SYSLOG_MSG_BYTES 128
/// @def LOG_SYSLOG_ALLOC_BYTES
/// @brief syslog message len  (if available)
#define LOG_SYSLOG_ALLOC_BYTES 512

/// @def LOG_REC_SEP_DFL
/// @brief default log record separator
#define LOG_REC_SEP_DFL "\r\n"
/// @def LOG_REC_DEL_DFL
/// @brief default log record field delimiter
#define LOG_REC_DEL_DFL ","
/// @def LOG_SUFFIX_DFL
/// @brief default log file suffix
#define LOG_SUFFIX_DFL "log"
/// @def LOG_SEG_SUFFIX_DFL
/// @brief default segmented log file suffix
#define LOG_SEG_SUFFIX_DFL "000"
/// @def LOG_ZBINLOG_NAME_DFL
/// @brief default (parsed) data log file name
#define LOG_ZBINLOG_NAME_DFL "ZBIN"
/// @def LOG_SYSLOG_NAME_DFL
/// @brief default system log file name
#define LOG_SYSLOG_NAME_DFL "SYS"
/// @def LOG_RAWLOG_NAME_DFL
/// @brief default raw data log file name
#define LOG_RAWLOG_NAME_DFL "RAW"
/// @def LOG_OBINLOG_NAME_DFL
/// @brief default raw data log file name
#define LOG_OBINLOG_NAME_DFL "OBIN"
/// @def LOG_CYCLOG_NAME_DFL
/// @brief default cycle log file name
#define LOG_CBINLOG_NAME_DFL "CBIN"
/// @def LOG_ZBINSEQ_NAME
/// @brief default data log sequence number file name
#define LOG_ZBINSEQ_NAME "ZBQ"
/// @def LOG_SYSSEQ_NAME
/// @brief default system log sequence number file name
#define LOG_SYSSEQ_NAME "SYSSEQ"
/// @def LOG_RAWSEQ_NAME
/// @brief default raw data log sequence number file name
#define LOG_RAWSEQ_NAME "RAWSEQ"
/// @def LOG_OBINSEQ_NAME
/// @brief default orecord binary data log sequence number file name
#define LOG_OBINSEQ_NAME "OBQ"
/// @def LOG_CYCSEQ_NAME
/// @brief default cycle ID )sequence number) file name
#define LOG_CYCSEQ_NAME "CYQ"
/// @def LOG_HRANGE_VALID
/// @brief return true if log handle is valid handle
/// @param hnd handle to test
#define LOG_HRANGE_VALID(hnd) ( ((hnd>=0) && (hnd<LOG_HND_COUNT)) ? true : false)

/// @def LOG_QUEUE_DATA_BYTES
/// @brief nominal queue entry data size (for queue allocation)
/// [must hold largest buffered data]
#define QUEUE_MEAS_ENTRY_BYTES LI_ZREC_SIZEN(1)
/// @def LOG_QUEUE_DEPTH
/// @brief number of data queue entries
#define QUEUE_MEAS_DEPTH 12

/// @def BIN_QUEUE_DATA_BYTES
/// @brief nominal queue entry data size (for queue allocation)
/// [must hold largest buffered data]
#define QUEUE_CYCLE_ENTRY_BYTES LI_ZREC_SIZEN(LICOR_MAX_CYCLE_MEASUREMENTS)
/// @def BIN_QUEUE_DEPTH
/// @brief number of data queue entries
#define QUEUE_CYCLE_DEPTH 1

/// @def LOG_MAX_SEG_N
/// @brief max number of log segments
/// [limited by FAT 8.3 file name - 3 digits max]
#define LOG_MAX_SEG_N 999

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

/// @typedef struct log_s log_t
/// @brief log type structure
typedef struct log_s{
    /// @var log_s::fp_dat
    /// @brief data file pointer
    FILE_T *fp_dat;
    /// @var log_s::fp_seq
    /// @brief data sequence number file pointer
    FILE_T *fp_seq;
    /// @var log_s::name
    /// @brief data file name
    const char *name;
    /// @var log_s::name_seq
    /// @brief sequence number file name
    char *name_seq;
    /// @var log_s::rec_sep
    /// @brief record separator
    const char *rec_sep;
    /// @var log_s::seg_size
    /// @brief max file size (0: unlimited)
    unsigned long seg_size;
    /// @var log_s::segment
    /// @brief current segment
    unsigned long segment;
    /// @var log_s::is_open
    /// @brief true if file is open
    bool is_open;
}log_t;

/// @enum log_handle_t 
/// @brief Log handles.
typedef enum{
    LOG_HND_INVALID=-1,
    LOG_ZBIN=0,
    LOG_SYSLOG,
    LOG_RAW,
    LOG_OBIN,
    LOG_CBIN,
    LOG_HND_COUNT
}log_handle_t;

/// @enum queue_handle_t 
/// @brief Data queue handles.
typedef enum{
    QUEUE_INVALID=-1,
    QUEUE_MEAS=0,
    QUEUE_CYCLE,
    QUEUE_COUNT
 }queue_handle_t;


/// @typedef struct queue_entry_s queue_entry_t
/// @brief queue entry container 
typedef struct queue_entry_s{
    /// @var queue_entry_s::len
    /// @brief data entry length (bytes)
   size_t len;
    /// @var queue_entry_s::id
    /// @brief data entry ID (user-defined)
    int id;
    /// @var queue_entry_s::data
    /// @brief data entry storage
    byte *data;
}queue_entry_t;

/// @typedef struct data_queue_s data_queue_s
/// @brief generic FIFO queue. 
/// push stores data in the queue; entries are overwritten when full.
/// pop retrieves and deletes the oldest entry in the queue.
/// peek retrieves, but doesn't delete a specified entry.
/// count returns the number of entries currently in the queue.
typedef struct data_queue_s{
    
    /// @var data_queue_s::depth
    /// @brief number of queue entries
    size_t depth;
    /// @var data_queue_s::entry_size
    /// @brief size of entry data (bytes)
    size_t entry_size;
    /// @var data_queue_s::entries
    /// @brief pointer to entries
    queue_entry_t *entries;
    /// @var data_queue_s::entry_mem
    /// @brief pointer to entry data storage
    byte *entry_mem;
    /// @var data_queue_s::count
    /// @brief current number of entries
    uint32_t count;
    /// @var data_queue_s::push
    /// @brief queue input pointer
    queue_entry_t *push;
    /// @var data_queue_s::pop
    /// @brief queue output pointer
    queue_entry_t *pop;
}data_queue_t;

/// @enum log_flags_t 
/// @brief log function option flags
typedef enum{
    LF_NONE=0x0,
    LF_WITH_REC_LEN=0x1,
    LF_NOCLOSE=0x2,
    LF_WITHOUT_REC_SEP=0x4,
    LF_WITHOUT_REC_DEL=0x8
}log_flags_t;

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

#ifdef __cplusplus
extern "C" {
#endif

/// @fn uint32_t log_max_seg(log_handle_t hnd)
/// @brief return greatest log segment number
/// @param hnd log handle 
/// @return greatest segment number
uint32_t log_max_seg(log_handle_t hnd);

/// @fn char *log_file_name(log_handle_t hnd, char *dest)
/// @brief return log file name for hnd
/// @param hnd log handle 
/// @param dest destination buffer (must hold LOG_PATH_BYTES) 
/// @return hand file name
char *log_file_name(log_handle_t hnd, char *dest);

/// @fn int log_get_seq_number(log_handle_t hnd, uint32_t *r_seq, bool increment)
/// @brief return next log sequence number
/// @param hdn log handle 
/// @param r_seq return (next) sequence number in r_seq if non-NULL.
/// @param update increment sequence number if true, otherwise don't change
/// @return 0 on success, -1 otherwise
int log_get_seq_number(log_handle_t hnd, uint32_t *r_seq, bool increment);
int log_fget_seq_number(FILE_T *fp, char *path, uint32_t *r_seq, bool increment);

/// @fn int log_set_seq_number(log_handle_t hnd, uint32_t seq_n)
/// @brief set log sequence number file contents
/// @param hdn log handle 
/// @param seq_n new sequence number.
/// @return 0 on success, -1 otherwise
int log_set_seq_number(log_handle_t hnd, uint32_t seq_n);
int log_fset_seq_number(FILE_T *fp, char *path, uint32_t seq_n);

/// @fn return_code_t log_adds(log_handle_t hnd, char* record, Time *time)
/// @brief send NULL terminated string to the data log file
/// @param hdn log handle 
/// @param data log entry data
/// @param time timestamp (struct tm)
/// @param flags options (e.g. LF_WR_LEN)
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_adds(log_handle_t hnd, char* data, Time *time, log_flags_t flags);

/// @fn return_code_t log_addn(log_handle_t hnd, char* record, Time *time, uint32_t payload_bytes)
/// @brief send data  bytes to the data log file
/// @param hdn log handle 
/// @param data log entry data
/// @param len record data bytes
/// @param time timestamp (struct tm)
/// @param flags options (e.g. LF_WR_LEN)
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_addb(log_handle_t hnd, byte* data, size_t len, Time *time, log_flags_t flags);

/// @fn return_code_t log_add(log_handle_t hnd, char* record, Time *time, uint32_t payload_bytes)
/// @brief send data  bytes to the data log file
/// @param hdn log handle 
/// @param data log entry data
/// @param data_len record data bytes
/// @param time timestamp (struct tm)
/// @param del delimiter string
/// @param sep record separator string
/// @param flags options (e.g. LF_WR_LEN)
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_addx(log_handle_t hnd, byte* data, size_t data_len, Time *time, const char *del, const char *sep, log_flags_t flags);

/// @fn int log_syslog(char *fmt, ... );
/// @brief write a formatted message to the syslog (e.g. like printf)
/// @param fmt printf format string
/// @return number of bytes written
int log_syslog(char *fmt, ... );
int log_printf(log_handle_t hnd, char *fmt, ... );

/// @fn return_code_t log_init()
/// @brief initialize logs
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_init();

/// @fn return_code_t log_open(log_handle_t hnd)
/// @brief open log (usually done automatically when adding)
///        this enables pre-opening files during sampling
/// @param hdn log handle 
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_open(log_handle_t hnd);

/// @fn uint32_t log_close(log_handle_t hnd)
/// @brief close log (use after passing LF_NOCLOSE to log_addx)
/// @param hdn log handle 
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_close(log_handle_t hnd);
return_code_t log_truncate(log_handle_t hnd);

/// @fn uint32_t log_bytes(log_handle_t hnd)
/// @brief get log record size (bytes)
/// @param hdn log handle 
/// @return ERR_SUCCESS on success, error code otherwise
uint32_t log_bytes(log_handle_t hnd);

/// @fn void log_show(log_handle_t hnd, bool verbose, int indent)
/// @brief show log summary to console
/// @param hdn log handle 
/// @param verbose enable verbose output
/// @param indent indent output (spaces) 
/// @return ERR_SUCCESS on success, error code otherwise
void log_show(log_handle_t hnd, bool verbose, int indent);

/// @fn uint32_t log_err_flags()
/// @brief return log error flags set during last initialization
/// @return error flags
uint32_t log_err_flags();

/// @fn return_code_t log_set_segsize(log_handle_t hnd, uint32_t val)
/// @brief set segment size for specified log
/// @param hdn log handle 
/// @param val new value
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_set_segsize(log_handle_t hnd, uint32_t val);

/// @fn uint32_t log_segsize(log_handle_t hnd,  uint32_t *dest)
/// @brief return segment size for specified log
/// @param hdn log handle 
/// @param dest pointer to dest
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_segsize(log_handle_t hnd, uint32_t *dest);

/// @fn uint32_t log_rec_count(log_handle_t hnd)
/// @brief get log record size (bytes)
/// @param hdn log handle 
/// @return ERR_SUCCESS on success, error code otherwise
//uint32_t log_rec_count(log_handle_t hnd);
//uint32_t log_record_count(log_handle_t hnd, time_t start, time_t end);
//uint32_t log_nget_records(log_handle_t hnd, uint32_t start, uint32_t n);
//uint32_t log_tget_records(log_handle_t hnd, time_t start, time_t end);
//uint32_t log_last_records(log_handle_t hnd, uint32_t n);

/// @fn return_code_t log_queue_init()
/// @brief initialize log queue
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_queue_init();

/// @fn void log_queue_show(queue_handle_t hnd, bool verbose, int indent)
/// @brief show log queue summary to console
/// @param hnd queue handle
/// @param verbose use verbose output
/// @param indent number of spaces to indent
/// @return none
void log_queue_show(queue_handle_t hnd, bool verbose, int indent);

/// @fn void log_qentry_show(queue_entry_t *self, bool verbose, int indent)
/// @brief show log queue entry summary to console
/// @param self queue entry reference
/// @param verbose use verbose output
/// @param indent number of spaces to indent
/// @return none
void log_qentry_show(queue_entry_t *self, bool verbose, int indent);

/// @fn return_code_t log_qshow_cmd(char *arg)
/// @brief log_queue_show() wrapper satisfying CmdFunc interface
/// @return ERR_SUCCESS on success, error code otherwise
/// @param argc number of arguments
/// @param argv argument list (strings)
return_code_t log_qshow_cmd(int argc, char **argv);

/// @fn int log_queue_type(uint32_t idx)
/// @brief return entry record type
/// @param hnd queue handle
/// @param idx queue entry index 
/// @return log queue type ID
int log_queue_type(queue_handle_t hnd, uint32_t idx);

/// @fn size_t log_queue_len(uint32_t idx)
/// @brief return entry record size (bytes)
/// @param hnd queue handle
/// @param idx queue entry index 
/// @return entry size
size_t log_queue_len(queue_handle_t hnd, uint32_t idx);

/// @fn uint32_t log_queue_count()
/// @brief return number of entries in queue
/// @param hnd queue handle
/// @return number of queue entries
uint32_t log_queue_count(queue_handle_t hnd);

/// @fn size_t log_queue_entry_size()
/// @brief return entry size (bytes)
/// @param hnd queue handle
/// @return size of queue entries
size_t log_queue_entry_size(queue_handle_t hnd);

/// @fn return_code_t log_queue_push(byte *src, int id, size_t len)
/// @brief add data into queue
/// @param hnd queue handle
/// @param src data pointer 
/// @param id data type 
/// @param len data len (bytes)
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_queue_push(queue_handle_t hnd, byte *src, int id, size_t len);

/// @fn return_code_t log_queue_peek(uint32_t idx, queue_entry_t *dest)
/// @brief get queue entry
/// @param hnd queue handle
/// @param idx queue entry index 
/// @param dest queue entry to hold data
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_queue_peek(queue_handle_t hnd, int32_t idx, queue_entry_t *dest);

/// @fn return_code_t log_queue_pop(queue_entry_t *dest)
/// @brief get next (oldest) queue entry and delete it from the queue
/// @param hnd queue handle
/// @param dest queue entry to hold data
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_queue_pop(queue_handle_t hnd, queue_entry_t *dest);

/// @fn return_code_t log_queue_clear(queue_handle_t hnd)
/// @brief delete all queue entries
/// @param hnd queue handle
/// @return ERR_SUCCESS on success, error code otherwise
return_code_t log_queue_clear(queue_handle_t hnd);


#ifdef __cplusplus
}
#endif

#endif // THIS_FILE
