///
/// @file lutil_api.c
/// @authors k. headley
/// @date 01 mar 2020

/// Log utility front end APIs
/// (format-specific implementations defined in lutil_impl.h,c)

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


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

 Copyright 2002-2020 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.
 */

/////////////////////////
// Headers
/////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
// for isspace(trim)
#include <ctype.h>
#include "cwg-shared.h"
#include "licor8xx-shared.h"
#include "lutil_api.h"
#include "lutil_impl.h"
#include "iowfile.h"
#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
#include "log.h"
#include "ioserial.h"
#include "timers.h"
#endif
#if defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__)
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#endif
#if defined(__WIN32) || defined(__WIN64)
#include <Windows.h>
#endif


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

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "TBD_PRODUCT"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-YYYY MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

/////////////////////////
// Declarations
/////////////////////////

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////

/////////////////////////
// Function Definitions
/////////////////////////

bool g_logutils_interrupt=false;

char *lutil_dfl_file(lutil_type_id type, char *dest, uint32_t len)
{
    char *retval=NULL;
    if(NULL!=dest){

 #if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
             switch(type){
                case LU_ZBIN:
                    if(len>=LOG_PATH_BYTES)
                        retval = log_file_name(LOG_ZBIN,dest);
                    break;
                case LU_OBIN:
                    if(len>=LOG_PATH_BYTES)
                        retval = log_file_name(LOG_OBIN,dest);
                    break;
                default:
                    break;
            }
 #endif

 #if defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__)
        struct tm *now=NULL;
        time_t tt=0;
          switch(type){
               case LU_ZBIN:
                    if(len>=13){
                        time(&tt);
                        now=gmtime(&tt);
                        sprintf(dest, "%s%.2d%.2d.%03lu", "ZBIN",now->tm_year%100, now->tm_mon+1,0UL);
                        retval=dest;
                    }
                    break;
                case LU_OBIN:
                    if(len>=13){
                        time(&tt);
                        now=gmtime(&tt);
                        sprintf(dest, "%s%.2d%.2d.%03lu", "OBIN",now->tm_year%100, now->tm_mon+1,0UL);
                        retval=dest;
                     }
                    break;
                default:
                    break;
            }
#endif
    }
    return retval;
}

return_code_t lutil_lless(int opt_n, char **opts)
{
    return_code_t retval=ERR_FAILURE;

    // set debug default
	// SET_XPRINT(0xFFFFFFFF);

    log_impl_t log_impl_s, *impl=&log_impl_s;

    lutil_opts_t cfg_s={
        false,
        0,-1,
        QOF_PRETTY|QOF_CONSOLE|QOF_HEADER,
        LINF_TIME_POSIX|LINF_FMT_PRETTY,
        OF_NONE,
        LU_INVALID,
        NULL,
        {0,0,0,0,{{0},{0}}}
    }, *cfg=&cfg_s;

    int test=0;
    if( (test=limpl_parse_lutil_opts(cfg,opt_n, opts))==0){

        if(NULL==cfg->src){
             char dfl_path[128]={0};
             cfg->src=lutil_dfl_file(cfg->src_type, dfl_path, 128);
        }

		if( (g_xprint&0xFE) != 0 )
        lutil_show_opts(cfg);

        if(limpl_init(impl, cfg->src, cfg->src_type) == 0 ){
            if((retval=limpl_lless(impl, cfg))!=0){
                XTRACE(0xFE);
            }
        }else{
            XTRACE(0xFE);
        }
    }else{
        if(test==1){
            OUT_CONS(" %s : <type> [<options>] [src:]<file>\r\n",opts[0]);
            OUT_CONS("  type      : log type O|Z\r\n");
            OUT_CONS("  file      : log file path\r\n");
            OUT_CONS(" options:\r\n");
            OUT_CONS("  h         : help\r\n");
            OUT_CONS("  out:<fmt> : set one or more output flags:\r\n");
            OUT_CONS("          p - pretty format (default)\r\n");
            OUT_CONS("          b - base85 format\r\n");
            OUT_CONS("          c - CSV format\r\n");
            OUT_CONS("          m - Mixed CSV/hex format\r\n");
            OUT_CONS("          u - UUENCODE format\r\n");
            OUT_CONS("          x - hex format (single line)\r\n");
            OUT_CONS("         xx - pretty hex format (multiple lines, indexed)\r\n");
            OUT_CONS("         x* - hex+pretty hex format\r\n");
            OUT_CONS("          r - raw (binary) format (not recommended for console)\r\n");
            OUT_CONS("          h - header\r\n");
            OUT_CONS("          d - data\r\n");
            OUT_CONS("          v - verbose\r\n");
            OUT_CONS(" defaults: out:PDHV s0 c-1 (all))\r\n");
            OUT_CONS("\r\n");
        }else{
            XTRACE(0xFE);
        }
    }

    return retval;
}

return_code_t lutil_lcat(int opt_n, char **opts)
{
    return_code_t retval=ERR_FAILURE;

    // set debug default
    // SET_XPRINT(0xFFFFFFFF);

    log_impl_t log_impl_s, *impl=&log_impl_s;

    lutil_opts_t cfg_s={
        false,
        0,-1,
        QOF_PRETTY|QOF_CONSOLE|QOF_HEADER,
        LINF_TIME_POSIX|LINF_FMT_PRETTY,
        OF_NONE,
        LU_INVALID,
        NULL,
        {0,0,0,0,{{0},{0}}}
    }, *cfg=&cfg_s;

    int test=0;
    if( (test=limpl_parse_lutil_opts(cfg,opt_n, opts))==0){

        if(NULL==cfg->src){
             char dfl_path[128]={0};
             cfg->src=lutil_dfl_file(cfg->src_type, dfl_path, 128);
        }

        if( (g_xprint&0xFE) != 0 )
        lutil_show_opts(cfg);

        if(limpl_init(impl, cfg->src, cfg->src_type) == 0 ){
            if((test=limpl_head(impl))==0){
               retval=impl->nshow(impl, 5, cfg);
            }else{
                XTRACE(0xFE);
            }
        }else{
            XTRACE(0xFE);
        }
    }else{
        if(test==1){
            OUT_CONS(" %s : <type> [<options>] [src:]<file>\r\n",opts[0]);
            OUT_CONS(" required:\r\n");
            OUT_CONS("  type      : log type O|Z\r\n");
            OUT_CONS("  file      : log file path\r\n");
            OUT_CONS(" options:\r\n");
            OUT_CONS("  h         : help\r\n");
            OUT_CONS("  l<n>      : last n\r\n");
            OUT_CONS("  s<n>      : start index\r\n");
            OUT_CONS("  c<n>      : count (number of records)\r\n");
            OUT_CONS(" filters: <type><op><a>[,<b>]\r\n");
            OUT_CONS("  ti        : ISO8601 time\r\n");
            OUT_CONS("  te        : epoch millisec\r\n");
            OUT_CONS("  rt        : record type\r\n");
            OUT_CONS("  rm        : record id\r\n");
            OUT_CONS("  mm        : measurement id\r\n");
            OUT_CONS("  esn       : ensemble sequence number\r\n");
            OUT_CONS("  sn        : sequence number\r\n");
            OUT_CONS("  idx       : index number\r\n");
            OUT_CONS("  dbg:<x>   : debug mask (hex)\r\n");
            OUT_CONS("  gmt:<n>   : gmt offset (h)\r\n");
            OUT_CONS("  out:<fmt> : set one or more output flags:\r\n");
            OUT_CONS("             p - pretty format (default)\r\n");
            OUT_CONS("             b - base85 format\r\n");
            OUT_CONS("             c - CSV format\r\n");
            OUT_CONS("             u - UUENCODE format\r\n");
            OUT_CONS("             x - hex format (single line)\r\n");
            OUT_CONS("             xx - pretty hex format (multiple lines, indexed)\r\n");
            OUT_CONS("             x* - hex+pretty hex format\r\n");
            OUT_CONS("             r - raw (binary) format (not recommended for console)\r\n");
            OUT_CONS("             h - header\r\n");
            OUT_CONS("             d - data\r\n");
            OUT_CONS("             v - verbose (show index, ofs where supported)\r\n");
            OUT_CONS(" defaults: out:PDHV s0 c-1 (all))\r\n");
            OUT_CONS("\r\n");
        }else{
            XTRACE(0xFE);
        }
    }

    return retval;
}

return_code_t lutil_linspect(int opt_n, char **opts)
{
    return_code_t retval=ERR_FAILURE;

    // set debug default
    // SET_XPRINT(0xFFFFFFFF);

    log_impl_t log_impl_s, *impl=&log_impl_s;

    lutil_opts_t cfg_s={
        false,
        0,-1,
        QOF_PRETTY|QOF_CONSOLE|QOF_HEADER,
        LINF_TIME_POSIX|LINF_FMT_PRETTY,
        OF_NONE,
        LU_INVALID,
        NULL,
        {0,0,0,0,{{0},{0}}}
    }, *cfg=&cfg_s;

    int test=0;
    if( (test=limpl_parse_lutil_opts(cfg,opt_n, opts))==0){

        if(NULL==cfg->src){
             char dfl_path[128]={0};
             cfg->src=lutil_dfl_file(cfg->src_type, dfl_path, 128);
        }

        if( (g_xprint&0xFE) != 0 )
        lutil_show_opts(cfg);

        if(limpl_init(impl, cfg->src, cfg->src_type) == 0 ){
            if(impl->inspect(impl, 3, cfg)==0){
                retval=0;
            }
        }else{
            XTRACE(0xFE);
        }
    }else{
        if(test==1){
            OUT_CONS(" %s : <type> [<options>] file...\r\n",opts[0]);
            OUT_CONS("  O|Z     : log type\r\n");
            OUT_CONS(" options:\r\n");
            OUT_CONS("  h       : help\r\n");
            OUT_CONS("  out:<f> : set one or more output flags:\r\n");
            OUT_CONS("          p - pretty format (default)\r\n");
            OUT_CONS("          r - record counter\r\n");
            OUT_CONS("          e - epoch (posix time)\r\n");
            OUT_CONS("          i - ISO8601 time\r\n");
            OUT_CONS("          c - CSV format\r\n");
            OUT_CONS("          h - output CSV header\r\n");
            OUT_CONS("\r\n");
            OUT_CONS(" defaults: out:PDHV\r\n");
        }else{
            XTRACE(0xFE);
        }
    }

    return retval;
}

#ifdef WITH_DQUTIL
return_code_t lutil_dqutil(int opt_n, char **opts)
{
    return_code_t retval=ERR_FAILURE;

    // set debug default
    // SET_XPRINT(0xFFFFFFFF);

    log_impl_t log_impl_s, *impl=&log_impl_s;

    dqutil_opts_t cfg_s={
        DQC_INVALID,
        true,
        0,1,
        QOF_PRETTY|QOF_CONSOLE|QOF_HEADER,
        OF_NONE,
        0,false,
        LU_INVALID,
        NULL,NULL,
        {0,0,0,0,{{0},{0}}}
   }, *cfg=&cfg_s;

    int test=0;
    if( (test=limpl_parse_dqutil_opts(cfg,opt_n, opts))==0){

        if( (g_xprint&0xFE) != 0 )
        dqutil_show_opts(cfg);

        if(limpl_init(impl, cfg->src, cfg->src_type) == 0 ){
            if(limpl_dqutil(impl,cfg)==0){
                retval=ERR_SUCCESS;
            }
        }

    }else{
        if(test==1){
            OUT_CONS(" %s : <op> <type> [<options>] src:<file> dest:<file>\r\n",opts[0]);
            OUT_CONS(" required:\r\n");
            OUT_CONS("  type    :  log type O|Z\r\n");
            OUT_CONS("  op      :  queue operation (ADD,DEL,MOVE,SHOW,INSPECT)\r\n");
            OUT_CONS("  src     :  source (log)\r\n");
            OUT_CONS("  dest    :  dest (queue)\r\n");
            OUT_CONS(" options:\r\n");
            OUT_CONS("  h       : help\r\n");
            OUT_CONS("  l<n>    : last n\r\n");
            OUT_CONS("  s<n>    : start index\r\n");
            OUT_CONS("  c<n>    : count (number of records)\r\n");
            OUT_CONS(" filters: <filter><op><a>[,<b>]\r\n");
            OUT_CONS("  ti        : ISO8601 time\r\n");
            OUT_CONS("  te        : epoch millisec\r\n");
            OUT_CONS("  rt        : record type\r\n");
            OUT_CONS("  rm        : record id\r\n");
            OUT_CONS("  mm        : measurement id\r\n");
            OUT_CONS("  esn       : ensemble sequence number\r\n");
            OUT_CONS("  sn        : sequence number\r\n");
            OUT_CONS("  idx       : index number\r\n");
            OUT_CONS("  out:<f> : set one or more output flags:\r\n");
            OUT_CONS("            p - pretty format (default)\r\n");
            OUT_CONS("            b - base85 format\r\n");
            OUT_CONS("            c - CSV format\r\n");
            OUT_CONS("            u - UUENCODE format\r\n");
            OUT_CONS("            x - hex format (single line)\r\n");
            OUT_CONS("           xx - pretty hex format (multiple lines, indexed)\r\n");
            OUT_CONS("           x* - hex+pretty hex format\r\n");
            OUT_CONS("            r - raw (binary) format (not recommended for console)\r\n");
            OUT_CONS("            h - header\r\n");
            OUT_CONS("            d - data\r\n");
            OUT_CONS("            v - verbose (show index, ofs where supported)\r\n");
            OUT_CONS(" defaults: l1 out:PHV\r\n");
            OUT_CONS("\r\n");
        }else{
            XTRACE(0xFE);
        }
    }

    return retval;
}
#endif //WITH_DQUTIL
