//
/// @file b85fmt.c
/// @authors k. Headley
/// @date 05 dec 2019

/// Show base85 orecord and zrecord data in various formats

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

/////////////////////////
// Headers
/////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <termios.h>
#include <unistd.h>

#include "z85.h"
#include "licor8xx-shared.h"
#include "cwg-shared.h"

/////////////////////////
// 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 "carbonwg"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-2019 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"
*/

#define RECORD_BUF_BYTES 2048
#define LINE_BUF_BYTES 2048
#define INDENT_DFL 0
#define OFLAG_DFL (QOF_NONE)
#define VERBOSE_DFL 0
#define VMSG(n,fmt,...) if(n<=g_verbose)fprintf(stderr,fmt,__VA_ARGS__)
#define VSTR(n,msg) if(n<=g_verbose)fprintf(stderr,"%s",msg)

/////////////////////////
// Declarations
/////////////////////////
typedef enum{UNKNOWN=0,OREC,ZREC,TYPE_COUNT}data_type_id;
typedef enum{ST_INVALID=0,ST_ZOK=0x1,ST_OOK=0x2}stat_flag_t;

typedef struct s_cfg{
    char *bname;
    stat_flag_t st_flags;
	queue_output_flag st_oflags;
	data_type_id st_type;
	int indent;
}cfg_t;

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

/////////////////////////
// Module Global Variables
/////////////////////////
static int g_verbose;

static char *type_str[]={"UNKNOWN","OREC","ZREC"};

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

// show formatted zrecord
int out_zrecord(licor_zrecord_t *src, cfg_t *cfg)
{
    int retval=0;

    if(NULL!=src && NULL!=cfg){
        if( (cfg->st_oflags&QOF_PRETTY) != 0 ){
            licor_zrecord_show((licor_zrecord_t *)src, true, cfg->indent);
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_PRETTY_HEX) != 0 ){
            sys_hexfmt_flag xflags = (XFMT_PRETTY|XFMT_OFFSET|XFMT_UCASE);
            licor_zrecord_outx((licor_zrecord_t *)src, xflags, cfg->indent, "\r\n");
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_CSV) != 0 ){
            licor_zrecord_outc((licor_zrecord_t *)src, 0, ",", "\r\n" );
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_HEX) != 0 ){
            sys_hexfmt_flag xflags = (XFMT_UCASE);
            licor_zrecord_outx((licor_zrecord_t *)src, xflags, cfg->indent, "\r\n");
            OUT_CTTY("\r\n");
        }
        OUT_CTTY("\r\n");
    }

    return retval;
}

// show formatted orecord
int out_orecord(licor_orecord_t *src, cfg_t *cfg)
{
    int retval=0;

    if(NULL!=src && NULL!=cfg){
        if( (cfg->st_oflags&QOF_PRETTY) != 0 ){
            licor_orecord_show((licor_orecord_t *)src, true, cfg->indent);
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_PRETTY_HEX) != 0 ){
            sys_hexfmt_flag xflags = (XFMT_PRETTY|XFMT_OFFSET|XFMT_UCASE);
            licor_orecord_outx((licor_orecord_t *)src, xflags, cfg->indent, "\r\n");
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_CSV) != 0 ){
            licor_orecord_outc((licor_orecord_t *)src, 0, ",", "\r\n" );
            OUT_CTTY("\r\n");
        }
        if( (cfg->st_oflags&QOF_HEX) != 0 ){
            sys_hexfmt_flag xflags = (XFMT_UCASE);
            licor_orecord_outx((licor_orecord_t *)src, xflags, cfg->indent, "\r\n");
            OUT_CTTY("\r\n");
        }
        OUT_CTTY("\r\n");
    }

    return retval;
}

// show help record
void show_help(char *bname)
{
    char help_fmt[]={ \
        "\r\n  %s : format DIC/pCO2 B85 zrecord and orecord data\r\n"
        "\r\n  use: %s [options...] <files...> [options...<files>...]\r\n"
        "\r\n  options:\r\n"
        "     h     - help\r\n"
        "     v<n>  - verbose output level\r\n"
        "     i<n>  - output indent level\r\n"
        "     o     - orecord input\r\n"
        "     z     - zrecord input\r\n"
        "     c     - show configuration\r\n"
        "     +/-c  - enable/disable CSV output\r\n"
        "     +/-p  - enable/disable pretty output\r\n"
        "     +/-x  - enable/disable hex output\r\n"
        "     +/-xx - enable/disable pretty hex output\r\n"
        "\r\n"
        "  Examples\r\n"
        "   # read orecords from a file, display as CSV\r\n"
        "     %s o +c $(cat o.b85)\r\n"
        "\r\n"
        "   # read zrecords from a file, display as pretty and pretty hex\r\n"
        "     %s z +p +xx $(cat z.b85)\r\n"
        "\r\n"
        "   # show orecords in pretty hex, then zrecords in pretty\r\n"
        "     %s o +xx $(cat o.b85) z -xx +p < z.b85 \r\n"
        "\r\n"
    };

    fprintf(stderr,help_fmt,bname,bname,bname,bname,bname);
}

void show_config(cfg_t *cfg, int verbose, int indent)
{
    if(NULL!=cfg){
        int wkey=15;
        int wval=15;

        OUT_CTTY("%*s%*s\r\n",indent,"", wkey, "--- config ---");
        OUT_CTTY("%*s%*s %*s\r\n",indent,"", wkey, "bname", wval, cfg->bname);
        OUT_CTTY("%*s%*s %*sx%08X\r\n",indent, "", wkey, "st_oflags", wval-9, "", cfg->st_oflags);
        char *tp =(cfg->st_type>0 && cfg->st_type<TYPE_COUNT ? type_str[cfg->st_type] :"");
        OUT_CTTY("%*s%*s %*s\r\n",indent, "", wkey, "type", wval, tp );
        OUT_CTTY("%*s%*s %*d\r\n",indent, "", wkey, "indent", wval, cfg->indent);
        OUT_CTTY("\r\n");
    }
}

// process one input argument
void process_arg( char *arg, cfg_t *cfg)
{
    if(NULL!=arg && NULL!=cfg){
        byte rec_buf[RECORD_BUF_BYTES]={0};
        byte *bp = rec_buf;

        // process options
        // note that options and input may
        // appear in any order, and options are sticky
        // i.e. remain in effect until changed
        if(strcmp(arg,"h")==0){
            show_help(cfg->bname);
        }else if(strcmp(arg,"o")==0){
            cfg->st_type=OREC;
        }else if(strcmp(arg,"z")==0){
            cfg->st_type=ZREC;
        }else if(strcmp(arg,"+c")==0){
            cfg->st_oflags |= QOF_CSV;
        }else if(strcmp(arg,"-c")==0){
            cfg->st_oflags &= ~QOF_CSV;
        }else if(strcmp(arg,"+p")==0){
            cfg->st_oflags |= QOF_PRETTY;
        }else if(strcmp(arg,"-p")==0){
            cfg->st_oflags &= ~QOF_PRETTY;
        }else if(strcmp(arg,"+x")==0){
            cfg->st_oflags |= QOF_HEX;
        }else if(strcmp(arg,"-x")==0){
            cfg->st_oflags &= ~QOF_HEX;
        }else if(strcmp(arg,"+xx")==0){
            cfg->st_oflags |= QOF_PRETTY_HEX;
        }else if(strcmp(arg,"-xx")==0){
            cfg->st_oflags &= ~QOF_PRETTY_HEX;
        }else if(strcmp(arg,"c")==0){
            show_config(cfg, true, 0);
        }else if(sscanf(arg,"i%d",&cfg->indent)==1){
        }else if(sscanf(arg,"v%d",&g_verbose)==1){
        }else{

            // treat as B85 record

            // B85 decode requires 5-byte alignment for input
            size_t src_len=strlen(arg);
            int pad =  ( (src_len%5) == 0 ? 0 : 5-(src_len%5) );
            uint32_t alen=src_len + pad;
            char sbuf[alen];
            memset(sbuf,0,alen);
            sprintf(sbuf,"%s",arg);

            VMSG(2,"src_len[%zd] pad[%d] alen[%"FMT_U32"]\r\n",src_len,pad,alen);

            // assume is data
            cfg->st_flags=ST_INVALID;
            if( NULL!=Z85_decode(rec_buf,RECORD_BUF_BYTES,sbuf,alen) ) {
                switch(cfg->st_type){
                    case OREC:
                        cfg->st_flags|=ST_OOK;
                        break;
                    case ZREC:
                        cfg->st_flags|=ST_ZOK;
                        break;
                    default:
                        WMSG("type not set");
                        break;
                }
            }

            // output orecord
            if(cfg->st_flags&ST_OOK){
                out_orecord((licor_orecord_t *)bp, cfg);
            }

            // output zrecord
            if(cfg->st_flags&ST_ZOK){
                out_zrecord((licor_zrecord_t *)bp, cfg);
            }
        }
    }
}


int chkin_termios(void)
{
    struct termios old, new;
    int ch;

    // save settings
    tcgetattr(STDIN_FILENO, &old);

    new = old;
    // non-canonical mode: inputs by char, not lines
    new.c_lflag &= ~ICANON;
    // wait for no bytes at all
    new.c_cc[VMIN] = 0;
    // timeout
    new.c_cc[VTIME] = 0;
    // new settings
    tcsetattr(STDIN_FILENO, TCSANOW, &new);

    // new settings
    ch = ungetc(getc(stdin), stdin);

    // restore settings
    tcsetattr(STDIN_FILENO, TCSANOW, &old);
    return (ch!=EOF);
}

int main(int argc, char **argv)
{
    int retval=-1;

    // get binary name
    char *bname = strrchr(argv[0],'/');
    if(NULL==bname){
        bname=argv[0];
    }else{
        bname++;
    }

    // default config
    cfg_t cfg_s={
        bname,
        ST_INVALID,
        OFLAG_DFL,
        UNKNOWN,
        INDENT_DFL
    }, *cfg=&cfg_s;

    // process input from cmdline
    int i=0;
    for(i=1;i<argc;i++){
        process_arg(argv[i],cfg);
    }

    // process input from stdin (if any)
    if(chkin_termios()){
        char line_buf[LINE_BUF_BYTES]={0};
        while(NULL!=fgets(line_buf,LINE_BUF_BYTES,stdin)){
            process_arg(line_buf,cfg);
            memset(line_buf,0,LINE_BUF_BYTES);
        }
    }

    return retval;
}
