///
/// @file cwg-shared.h
/// @authors k. headley
/// @date 03 mar 2020

/// Shared (cross-platform)  Carbon Waveglider (CWG) system functions and definitions

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


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

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

#ifndef CWG_SHARED_H
#define CWG_SHARED_H

/////////////////////////
// Includes
/////////////////////////
#include <time.h>
#include <stdint.h>
#include <stdbool.h>

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
#else
#include "stdio.h"
#endif

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

/// @def SYS_ASSIGN_MDFLAGS(mask)
/// @brief set module debug values to mask
/// @param mask new value (hex bit mask)
#define SYS_ASSIGN_MDFLAGS(mask) (g_mdebug_flags=mask)
/// @def SYS_SET_MDFLAGS(mask)
/// @brief set (OR) flag bits specified by mask
/// @param mask modules to enable 
#define SYS_SET_MDFLAGS(mask) (g_mdebug_flags|=mask)
/// @def SYS_CLR_MDFLAGS(mask)
/// @brief clear (AND ~) flag bits specified by mask
/// @param mask modules to disable 
#define SYS_CLR_MDFLAGS(mask) (g_mdebug_flags&=~(mask))
/// @def SYS_MDFLAGS 
/// @brief current module flag setting (as uint32_t)
#define SYS_MDFLAGS ((uint32_t)g_mdebug_flags)

/// @def SET_FLAGS(flags,mask)
/// @brief set bits in flags specified by mask
/// @param flags flag variable
/// @param mask bits to enable 
#define SET_FLAGS(flags,mask) (flags|=mask)
/// @def CLR_FLAGS(flags,mask)
/// @brief clear bits in flags specified by mask
/// @param flags flag variable
/// @param mask bits to enable 
#define CLR_FLAGS(flags,mask) flags&=(~(mask))
/// @def GET_FLAGS(flags,mask)
/// @brief apply mask to flags and return result
/// @param flags flag variable
/// @param mask bits to enable 
#define GET_FLAGS(flags,mask) (flags&mask)
/// @def ISSET(flags,mask)
/// @brief apply mask to flags and return true if any bits are set
/// @param flags flag variable
/// @param mask bits to enable 
#define ISSET(flags,mask) ( (flags&mask)==0 ? false : true )

/// @def SET_DPRINT(n)
/// @brief set dprint global variable to n (enable if non-zero)
/// @param n new value (int)
#define SET_DPRINT(n) g_dprint=n
/// @def SET_VPRINT(n)
/// @brief set vprint global variable to n (enable threshold <= n)
/// @param n new value (int)
#define SET_VPRINT(n) g_vprint=n
/// @def SET_XPRINT(n)
/// @brief set xprint global variable to n (enable threshold <= n)
/// @param n new value (32-bit hex)
#define SET_XPRINT(x) g_xprint=x

/// @typedef enum mod_id_t
/// @brief per-module debug output mask bits
typedef enum{
    MOD_ACT =0x00001,
    MOD_SEQ =0x00002,
    MOD_TMR =0x00004,
    MOD_SCR =0x00008,
    MOD_LIC =0x00010,
    MOD_SER =0x00020,
    MOD_CFG =0x00040,
    MOD_PRO =0x00080,
    MOD_SCH =0x00100,
    MOD_CRON=0x00200,
    MOD_MDIO=0x00400,
    MOD_MEAS=0x00800,
    MOD_MAIN=0x01000,
    MOD_TOKS=0x02000,
    MOD_SYS =0x04000,
    MOD_AUX =0x08000,
    MOD_LOG =0x10000,
    MOD_ALL=0xFFFF
}mod_id_t;

/// @def MMOD_IS_EN(mid)
/// @brief true if module ID mid is enabled
/// @param mid mod_it_t bit(s)
#define  MMOD_IS_EN(mid) ( mid&g_mdebug_flags ? true : false )

/// @def WITH_LOG_UTILS
/// @brief enable log utils: lloss, lcat, linfo, lseq
#define WITH_LOG_UTILS

/// @def WITH_XCAT
/// @brief enable extended cat implementation
#define WITH_XCAT

/// @def TM2ISO8601(ptm, dest)
/// @brief format Time as ISO8601 format string in dest
/// @param ptm pointer to Time
/// @param dest destination (min 21 bytes)
/// @return length of dest on success, -1 otherwise
#define TM2ISO8601(ptm, dest) sprintf(dest,"%04u-%02u-%02uT%02u:%02u:%02uZ",1900+ptm->tm_year, ptm->tm_mon+1, ptm->tm_mday,ptm->tm_hour, ptm->tm_min, ptm->tm_sec)

/// @def ZERO_MEM(p,sz)
/// @brief fill memory pointed to by p with zero
/// @param p pointer to memory
/// @param sz memory region length (bytes)
/// @return 0 on success, -1 otherwise
#define ZERO_MEM(p,sz)   memset(p,0,sz)

/// @def FILL_MEM(p,sz)
/// @brief fill memory pointed to by p with specified byte
/// @param p pointer to memory
/// @param c fill byte
/// @param sz memory region length (bytes)
/// @return 0 on success, -1 otherwise
#define FILL_MEM(p,c,sz) memset(p,c,sz)

/// @def BOOLCHAR(b)
/// @brief return 'Y' if b evaluates to true, 'N' otherwise
/// @param b boolean expression
#define BOOLCHAR(b) (b ? 'Y' : 'N')

/// @def SYS_ISSPC(c)
/// @brief return true if character is whitespace (SPC, TAB, CR, LF, VTAB, FFEED)
/// @param c byte
#define SYS_ISSPC(c) ((c==' '|| c=='\t'|| c=='\n'|| c=='\r'|| c=='\v'|| c=='\f') ? true : false)
/// @def SYS_ISQUOTE(c)
/// @brief return true if character is quote ('\'','\"')
/// @param c byte
#define SYS_ISQUOTE(c) ((c=='\''|| c=='\"') ? true : false)

/// @def U64_TIMESTAMP_SEC(ts)
/// @brief return timestamp seconds portion (uint64_t) 
/// @param ts timestamp uint64_t epoch msec
#define U64_TIMESTAMP_SEC(ts) (ts/1000LL)
/// @def U64_TIMESTAMP_SEC(ts)
/// @brief return timestamp milliseconds portion (uint64_t) 
/// @param ts timestamp uint64_t epoch msec
#define U64_TIMESTAMP_MSEC(ts) (ts%1000LL)
/// @def TIMESTAMP_STR_BYTES
/// @brief min timestamp string buffer length
#define TIMESTAMP_STR_BYTES 25

/// @def CTRL_D
/// @brief CTRL-D character value
#define CTRL_D 0x04

/// @def LICOR_MAX_CYCLE_MEASUREMENTS
/// @brief number of measurements in a LI-COR measurement cycle
/// used to set queue/buffer sizes, affects memory use
#define LICOR_MAX_CYCLE_MEASUREMENTS 6

#define CRC8_POLY 0x31
#define CRC8_INIT 0xFF

/// @typedef enum sys_iso8601_precision
/// @brief ISO8601 output precision
typedef enum{
    ISO8601_PREC_SEC=0,
    ISO8601_PREC_MSEC
}sys_iso8601_precision;

/// @typedef enum sys_hexfmt_flag
/// @brief hex output format flags
typedef enum{
    XFMT_NONE=0x0,
    XFMT_PRETTY=0x1,
    XFMT_OFFSET=0x2,
    XFMT_LCASE=0x4,
    XFMT_UCASE=0x8,
    XFMT_CRC8=0x10
}sys_hexfmt_flag;

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
// platform-dependent macros
// this set is for PIC24
//#warning ">>> Platform PIC24"

#ifndef TYPEDEF_BYTE
#define TYPEDEF_BYTE
/// @typedef uint8_t byte
/// @brief byte type 
typedef uint8_t byte;
#endif

// these macros define types for cross-platform use

/// @def FILE_T
/// @brief file stream type
#define FILE_T FIL
/// @def DIR_T
/// @brief directory type
#define DIR_T  DIR
/// @def DIRENT_T
/// @brief directory entry type
#define DIRENT_T  FILINFO
/// @def FINFO_T
/// @brief file system entry type
#define FINFO_T   FILINFO
/// @def FSYS_T
/// @brief file system type
#define FSYS_T FATFS

/// @def CRLF
/// @brief line ending
#define CRLF "\r\n"

/// @def LINEEND
/// @brief line ending 
#define LINEEND "\r\n"

// printf format macros
// these have different implementations per platform,
// to correctly format primitives of different sizes
// e.g. int is 16 bits on PIC but 32 bits on OSX.
//
// example: 
// int16_t my_int=10;
// sprintf(my_str,"my_int %"FMT_D16" \r\n",my_int);

#define FMT_D16  "d"
#define FMT_U16  "u"
#define FMT_D32  "ld"
#define FMT_U32  "lu"
#define FMT_U64  "llu"
#define FMT_D64  "lld"
#define FMT_SZ   "u"
#define FMT_FLAG "lX"
#define FMT_X16  "X"
#define FMT_X32  "lX"
#define FMT_XBP  "lX"
#define FMT_OFF  "lld"
#define FMT_FSFILCNT  "lu"

// platform-specific serial IO API macros

/// @def SER_ERR
/// @brief serial port for error output
#define SER_ERR g_ctty
/// @def SER_CTTY
/// @brief serial port for console output
#define SER_CTTY g_ctty
/// @def SER_TRACE
/// @brief serial port for trace output
#define SER_TRACE  stderr

#define TRACE() serPrintf(SER_ERR,"%s:%d\r\n",__FUNCTION__,__LINE__)
#define OUT_CONS(...) serPrintf(SER_CONS,__VA_ARGS__)
#define OUT_HOST(...) serPrintf(SER_HOST,__VA_ARGS__)
#define OUT_ERR(...)  serPrintf(SER_ERR,__VA_ARGS__)
#define OUT_CTTY(...) serPrintf(SER_CTTY,__VA_ARGS__)

#define MPRINT(mid,...) if(mid&g_mdebug_flags)OUT_CTTY(__VA_ARGS__)

#define DPRINT(...) if(g_dprint!=0)OUT_CTTY(__VA_ARGS__)
#define VPRINT(...) if(g_vprint!=0)OUT_CTTY(__VA_ARGS__)
#define VEPRINT(...)      if(g_vprint!=0)OUT_CTTY(__VA_ARGS__)
#define VLPRINT(lvl,...)  if(g_vprint>=lvl)OUT_CTTY(__VA_ARGS__)
#define VLEPRINT(lvl,...) if(g_vprint>=lvl)OUT_CTTY(__VA_ARGS__)

#define XTEST(msk)  ((g_xprint&msk)!=0 ? true :false)
#define XPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_CTTY(__VA_ARGS__)
#define XEPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_ERR(__VA_ARGS__)
#define XTRACE(msk)  if((g_xprint&msk)!=0)TRACE()

#define EMSG(msg) OUT_CTTY("%s:%d ERR - %s\r\n",__FUNCTION__,__LINE__,msg)
#define EPRINT(fmt,...) OUT_CTTY("%s:%d ERR - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)
#define WMSG(msg) OUT_CTTY("%s:%d WARN - %s\r\n",__FUNCTION__,__LINE__,msg)
#define WPRINT(fmt,...) OUT_CTTY("%s:%d WARN - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)

#define DSHOW_TOKS(ac,av) if(MOD_TOKS&g_mdebug_flags)sys_show_tokens(__FUNCTION__,ac,av)

#elif defined(__CYGWIN__)
//#warning ">>> Platform __CYGWIN__"

// platform-dependent macros
// this set is for POSIX systems

#ifndef TYPEDEF_BYTE
#define TYPEDEF_BYTE
/// @typedef unsigned char byte
/// @brief byte type
typedef unsigned char byte;
#endif

#ifndef TYPEDEF_DWORD
#define TYPEDEF_DWORD
/// @typedef uint32_t DWORD
/// @brief double word type
typedef uint32_t DWORD;
#endif
#ifndef TYPEDEF_UINT
#define TYPEDEF_UINT
/// @typedef uint16_t UINT
/// @brief unsigned 16-bit int type
typedef uint16_t UINT;
#endif

/// @def FILE_T
/// @brief file stream type
#define FILE_T FILE
/// @def DIR_T
/// @brief directory type
#define DIR_T  DIR
/// @def DIRENT_T
/// @brief directory entry type
#define DIRENT_T  struct dirent
/// @def FINFO_T
/// @brief file system entry type
#define FINFO_T struct stat
/// @def FSYS_T
/// @brief file system type
#define FSYS_T struct statvfs

/// @def CRLF
/// @brief line ending
#define CRLF "\r\n"

/// @def LINEEND
/// @brief line ending
#define LINEEND "\n"

/// @def const char* FILE_MODE
/// @brief file mode string
typedef const char* FILE_MODE;

// printf format macros
// these have different implementations per platform,
// to correctly format primitives of different sizes
// e.g. int is 16 bits on PIC but 32 bits on OSX.
//
// example:
// int16_t my_int=10;
// sprintf(my_str,"my_int %"FMT_D16" \r\n",my_int);

#define FMT_D16 "hd"
#define FMT_U16 "hu"
#define FMT_D32 "d"
#define FMT_U32 "u"
#define FMT_U64 "lu"
#define FMT_D64 "ld"
#define FMT_FSFILCNT  "lu"
#define FMT_FLAG "X"
#define FMT_X32 "X"
#define FMT_X16 "hX"
#define FMT_SZ  "zu"
#define FMT_XBP "lX"
#define FMT_OFF  "lld"

// platform-specific serial IO API macros

/// @def SER_CONS
/// @brief serial port for console output
#define SER_CONS stdout
/// @def SER_HOST
/// @brief serial port for host console output
#define SER_HOST stdout
/// @def SER_ERR
/// @brief serial port for error output
#define SER_ERR  stderr
/// @def SER_CTTY
/// @brief serial port for (controlling) console output
#define SER_CTTY stdout
/// @def SER_TRACE
/// @brief serial port for trace output
#define SER_TRACE  stderr

#define TRACE()    fprintf(SER_ERR,"%s:%d\n",__FUNCTION__,__LINE__)
#define OUT_CONS(...) fprintf(SER_CONS,__VA_ARGS__)
#define OUT_HOST(...) fprintf(SER_HOST,__VA_ARGS__)
#define OUT_ERR(...)  fprintf(SER_ERR,__VA_ARGS__)
#define OUT_CTTY(...) fprintf(SER_CTTY,__VA_ARGS__)

#define MPRINT(mid,...) if(mid&g_mdebug_flags)OUT_ERR(__VA_ARGS__)

#define DPRINT(...)       if(g_dprint!=0)OUT_ERR(__VA_ARGS__)
#define VPRINT(...)       if(g_vprint!=0)OUT_CONS(__VA_ARGS__)
#define VEPRINT(...)      if(g_vprint!=0)OUT_ERR(__VA_ARGS__)
#define VLPRINT(lvl,...)  if(g_vprint>=lvl)OUT_CONS__VA_ARGS__)
#define VLEPRINT(lvl,...) if(g_vprint>=lvl)OUT_ERR(__VA_ARGS__)

#define XPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_CTTY(__VA_ARGS__)
#define XEPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_ERR(__VA_ARGS__)
#define XTRACE(msk)  if((g_xprint&msk)!=0)TRACE()

#define EMSG(msg)       OUT_CTTY("%s:%d ERR - %s\r\n",__FUNCTION__,__LINE__,msg)
#define EPRINT(fmt,...) OUT_CTTY("%s:%d ERR - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)
#define WMSG(msg)       OUT_CTTY("%s:%d WARN - %s\r\n",__FUNCTION__,__LINE__,msg)
#define WPRINT(fmt,...) OUT_CTTY("%s:%d WARN - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)

/// @def VERSION_HELPER
#define VERSION_HELPER(s) #s
/// @def VERSION_STRING
#define VERSION_STRING(s) VERSION_HELPER(s)

/// @def WIN_DECLSPEC
/// @brief declaration for windows.
#define WIN_DECLSPEC __declspec(dllimport)

#elif defined(__unix__) || defined(__APPLE__) 
//#warning ">>> Platform __unix__ __APPLE__"

// platform-dependent macros
// this set is for POSIX systems

#ifndef TYPEDEF_BYTE
#define TYPEDEF_BYTE
/// @typedef unsigned char byte
/// @brief byte type
typedef unsigned char byte;
#endif

#ifndef TYPEDEF_DWORD
#define TYPEDEF_DWORD
/// @typedef uint32_t DWORD
/// @brief double word type
typedef uint32_t DWORD;
#endif
#ifndef TYPEDEF_UINT
#define TYPEDEF_UINT
/// @typedef uint16_t UINT
/// @brief unsigned 16-bit int type
typedef uint16_t UINT;
#endif

/// @def FILE_T
/// @brief file stream type
#define FILE_T FILE
/// @def DIR_T
/// @brief directory type
#define DIR_T  DIR
/// @def DIRENT_T
/// @brief directory entry type
#define DIRENT_T  struct dirent
/// @def FINFO_T
/// @brief file system entry type
#define FINFO_T struct stat
/// @def FSYS_T
/// @brief file system type
#define FSYS_T struct statvfs

/// @def CRLF
/// @brief line ending
#define CRLF "\r\n"

/// @def LINEEND
/// @brief line ending 
#define LINEEND "\n"

/// @def const char* FILE_MODE
/// @brief file mode string
typedef const char* FILE_MODE;

// printf format macros
// these have different implementations per platform,
// to correctly format primitives of different sizes
// e.g. int is 16 bits on PIC but 32 bits on OSX.
//
// example: 
// int16_t my_int=10;
// sprintf(my_str,"my_int %"FMT_D16" \r\n",my_int);

#define FMT_D16 "hd"
#define FMT_U16 "hu"
#define FMT_D32 "d"
#define FMT_U32 "u"
#define FMT_U64 "llu"
#define FMT_D64 "lld"
#define FMT_FSFILCNT  "u"
#define FMT_FLAG "X"
#define FMT_X32 "X"
#define FMT_X16 "hX"
#define FMT_SZ  "zu"
#define FMT_XBP "lX"
#define FMT_OFF  "lld"

// platform-specific serial IO API macros

/// @def SER_CONS
/// @brief serial port for console output
#define SER_CONS stdout
/// @def SER_HOST
/// @brief serial port for host console output
#define SER_HOST stdout
/// @def SER_ERR
/// @brief serial port for error output
#define SER_ERR  stderr
/// @def SER_CTTY
/// @brief serial port for (controlling) console output
#define SER_CTTY stdout
/// @def SER_TRACE
/// @brief serial port for trace output
#define SER_TRACE  stderr

#define TRACE()    fprintf(SER_ERR,"%s:%d\n",__FUNCTION__,__LINE__)
#define OUT_CONS(...) fprintf(SER_CONS,__VA_ARGS__)
#define OUT_HOST(...) fprintf(SER_HOST,__VA_ARGS__)
#define OUT_ERR(...)  fprintf(SER_ERR,__VA_ARGS__)
#define OUT_CTTY(...) fprintf(SER_CTTY,__VA_ARGS__)

#define MPRINT(mid,...) if(mid&g_mdebug_flags)OUT_ERR(__VA_ARGS__)

#define DPRINT(...)       if(g_dprint!=0)OUT_ERR(__VA_ARGS__)
#define VPRINT(...)       if(g_vprint!=0)OUT_CONS(__VA_ARGS__)
#define VEPRINT(...)      if(g_vprint!=0)OUT_ERR(__VA_ARGS__)
#define VLPRINT(lvl,...)  if(g_vprint>=lvl)OUT_CONS__VA_ARGS__)
#define VLEPRINT(lvl,...) if(g_vprint>=lvl)OUT_ERR(__VA_ARGS__)

#define XPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_CTTY(__VA_ARGS__)
#define XEPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_ERR(__VA_ARGS__)
#define XTRACE(msk)  if((g_xprint&msk)!=0)TRACE()

#define EMSG(msg)       OUT_CTTY("%s:%d ERR - %s\r\n",__FUNCTION__,__LINE__,msg)
#define EPRINT(fmt,...) OUT_CTTY("%s:%d ERR - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)
#define WMSG(msg)       OUT_CTTY("%s:%d WARN - %s\r\n",__FUNCTION__,__LINE__,msg)
#define WPRINT(fmt,...) OUT_CTTY("%s:%d WARN - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)

/// @def VERSION_HELPER
#define VERSION_HELPER(s) #s
/// @def VERSION_STRING
#define VERSION_STRING(s) VERSION_HELPER(s)
/// @def WIN_DECLSPEC
#define WIN_DECLSPEC

#elif defined(__WIN32) || defined(__WIN64)
#warning ">>> Platform __WIN32"
#ifndef TYPEDEF_BYTE
#define TYPEDEF_BYTE
/// @typedef unsigned char byte
/// @brief byte type
typedef unsigned char byte;
#endif
#ifndef TYPEDEF_DWORD
#define TYPEDEF_DWORD
/// @typedef uint32_t DWORD
/// @brief double word type
//typedef uint32_t DWORD;
#endif
#ifndef TYPEDEF_UINT
#define TYPEDEF_UINT
/// @typedef uint16_t UINT
/// @brief unsigned 16-bit int type
//typedef uint16_t UINT;
#endif

typedef unsigned long long fsblkcnt_t;
typedef unsigned long long fsfilcnt_t;

struct statvfs {
    unsigned long  f_bsize;    /* file system block size */
    unsigned long  f_frsize;   /* fragment size */
    fsblkcnt_t     f_blocks;   /* size of fs in f_frsize units */
    fsblkcnt_t     f_bfree;    /* # free blocks */
    fsblkcnt_t     f_bavail;   /* # free blocks for unprivileged users */
    fsfilcnt_t     f_files;    /* # inodes */
    fsfilcnt_t     f_ffree;    /* # free inodes */
    fsfilcnt_t     f_favail;   /* # free inodes for unprivileged users */
    unsigned long  f_fsid;     /* file system ID */
    unsigned long  f_flag;     /* mount flags */
    unsigned long  f_namemax;  /* maximum filename length */
};
/// @def FILE_T
/// @brief file stream type
#define FILE_T FILE
/// @def DIR_T
/// @brief directory type
#define DIR_T  DIR
/// @def DIRENT_T
/// @brief directory entry type
#define DIRENT_T  struct dirent
/// @def FINFO_T
/// @brief file system entry type
#define FINFO_T struct stat
/// @def FSYS_T
/// @brief file system type
#define FSYS_T struct statvfs

/// @def CRLF
/// @brief line ending
#define CRLF "\r\n"

/// @def LINEEND
/// @brief line ending
#define LINEEND "\n"

/// @def const char* FILE_MODE
/// @brief file mode string
typedef const char* FILE_MODE;

// printf format macros
// these have different implementations per platform,
// to correctly format primitives of different sizes
// e.g. int is 16 bits on PIC but 32 bits on OSX.
//
// example:
// int16_t my_int=10;
// sprintf(my_str,"my_int %"FMT_D16" \r\n",my_int);

#define FMT_D16 "hd"
#define FMT_U16 "hu"
#define FMT_D32 "d"
#define FMT_U32 "u"
#define FMT_U64 "llu"
#define FMT_D64 "lld"
#define FMT_FSFILCNT  "u"
#define FMT_FLAG "X"
#define FMT_X32 "X"
#define FMT_X16 "hX"
#define FMT_SZ  "zu"
#define FMT_XBP "lX"
#define FMT_OFF  "lld"

// platform-specific serial IO API macros

/// @def SER_CONS
/// @brief serial port for console output
#define SER_CONS stdout
/// @def SER_HOST
/// @brief serial port for host console output
#define SER_HOST stdout
/// @def SER_ERR
/// @brief serial port for error output
#define SER_ERR  stderr
/// @def SER_CTTY
/// @brief serial port for (controlling) console output
#define SER_CTTY stdout
/// @def SER_TRACE
/// @brief serial port for trace output
#define SER_TRACE  stderr

#define TRACE()    fprintf(SER_ERR,"%s:%d\n",__FUNCTION__,__LINE__)
#define OUT_CONS(...) fprintf(SER_CONS,__VA_ARGS__)
#define OUT_HOST(...) fprintf(SER_HOST,__VA_ARGS__)
#define OUT_ERR(...)  fprintf(SER_ERR,__VA_ARGS__)
#define OUT_CTTY(...) fprintf(SER_CTTY,__VA_ARGS__)

#define MPRINT(mid,...) if(mid&g_mdebug_flags)OUT_ERR(__VA_ARGS__)

#define DPRINT(...)       if(g_dprint!=0)OUT_ERR(__VA_ARGS__)
#define VPRINT(...)       if(g_vprint!=0)OUT_CONS(__VA_ARGS__)
#define VEPRINT(...)      if(g_vprint!=0)OUT_ERR(__VA_ARGS__)
#define VLPRINT(lvl,...)  if(g_vprint>=lvl)OUT_CONS__VA_ARGS__)
#define VLEPRINT(lvl,...) if(g_vprint>=lvl)OUT_ERR(__VA_ARGS__)

#define XPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_CTTY(__VA_ARGS__)
#define XEPRINT(msk,...)  if((g_xprint&msk)!=0)OUT_ERR(__VA_ARGS__)

#define EMSG(msg)       OUT_CTTY("%s:%d ERR - %s\r\n",__FUNCTION__,__LINE__,msg)
#define EPRINT(fmt,...) OUT_CTTY("%s:%d ERR - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)
#define WMSG(msg)       OUT_CTTY("%s:%d WARN - %s\r\n",__FUNCTION__,__LINE__,msg)
#define WPRINT(fmt,...) OUT_CTTY("%s:%d WARN - "fmt"\r\n",__FUNCTION__,__LINE__,__VA_ARGS__)

/// @def VERSION_HELPER
#define VERSION_HELPER(s) #s
/// @def VERSION_STRING
#define VERSION_STRING(s) VERSION_HELPER(s)
/// @def WIN_DECLSPEC
#define WIN_DECLSPEC __declspec(dllimport)

#elif defined(__WIN64)
#warning ">>> Platform __WIN64"

#endif // platform-specific


/////////////////////////
// Type Definitions
/////////////////////////
/// @enum return_code_t
/// @brief function return codes
/// OK
/// an error occurred
/// data missing
/// incomplete line
/// command not found
/// sub-command not found
/// function timed out
typedef enum {
    ERR_SUCCESS = 0,
    ERR_FAILURE,
    ERR_EMPTY_LINE,
    ERR_NO_LINE,
    ERR_NO_COMMAND,
    ERR_NO_SUB_COMMAND,
    ERR_TIME_OUT,
    ERR_NO_IMPL,
    ERR_INVALID,
    ERR_NOT_READY,
    ERR_NOT_FOUND,
    ERR_EOF,
    ERR_NOMEM,
    ERR_FILEIO
}return_code_t;

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

#ifdef __cplusplus
extern "C" {
#endif
    
    /// @var int g_dprint
    /// @brief global debug print macros
    // @sa DPRINT
    extern int g_dprint;
    /// @var int g_vprint
    /// @brief global verbose print 
    /// enable verbose print macros
    /// @sa VPRINT, VLPRINT, VEPRINT, VLEPRINT 
    extern int g_vprint;
    /// @var int g_xprint
    /// @brief global masked print 
    /// enable mask-selectable print macros
    /// @sa XPRINT, XEPRINT 
    extern uint32_t g_xprint;
    /// @var uint32_t g_mdebug_flags
    /// @brief module debug control
    /// set using  debug <+/-> <module(s)> 
    extern uint32_t g_mdebug_flags;
    
    /// @fn int hex_show(byte *data, size_t len, int cols, int indent, bool show_ofs);
    /// @brief display bytes as formatted hex
    /// @param[in]  data pointer to data
    /// @param[in]  len    number of data bytes
    /// @param[in]  col    number of columns to display
    /// @param[in]  indent number of spaces to indent output
    /// @param[in]  show_ofs show (hex) byte offsets if true
    /// @return number of bytes written on success, -1 otherwise
    int hex_show(byte *data, size_t len, int cols, int indent, bool show_ofs);

    /// @fn time_t parse_iso8601(const char *isotime, struct tm *rval)
    /// @brief parse iso8601 time string into struct tm
    /// @param[in]  isotime is8601 times string (YYYY-mm-ddThh:mm:ss)
    /// @param[in]  dest    struct tm destination
    /// @return time_t and struct tm time on success, (time_t)-1 otherwise (dest not set)
    time_t parse_iso8601(const char *isotime, struct tm *dest);

    /// @fn char *striso8601(char *dest, uint64_t timestamp, sys_iso8601_precision pflag)
    /// @brief
    /// @param[in]  dest      output buffer (a volatile statically allocated buffer
    ///                       will be used if NULL; contents may change between calls)
    /// @param[in]  timestamp uint64 time (time_t)
    /// @param[in]  pflag     indicate output precision/format (msec or sec))
    /// @return pointer to string on success, NULL otherwise
    char *striso8601(char *dest, uint64_t timestamp, sys_iso8601_precision pflag);

    /// @fn int tokenize(char *src, char **dest, char *del, int ntok)
    /// @brief Divide string into tokens. Modifies src.
    /// @param[in]  src  string to tokenize (is modified, delimiters are replaced by '\0')
    /// @param[out] dest pointer to array of token pointers
    /// @param[in]  del  delimiter string
    /// @param[in]  ntok maximum number of tokens to parse
    /// @return number of tokens parsed on success, -1 otherwise
    int tokenize(char *src, char **dest, char *del, int ntok);

    byte crc8(byte *data, uint32_t len);
    int bytes_to_xstr(byte *src, char *dest, uint32_t slen, uint32_t dlen, uint32_t flags);

#ifdef __cplusplus
}
#endif

#endif // THIS_FILE
