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

/// Serial IO wrapper API

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

// include guard (recommended)
#ifndef IOWSERIAL_H
#define IOWSERIAL_H

/////////////////////////
// Includes 
/////////////////////////
#include <stdbool.h>
#if defined(__WIN32) || defined(_WIN64)

//#include "stdafx.h"
#include <Windows.h>

#else
#include <termios.h>
#include <unistd.h>
#endif

#include "cwg-shared.h"

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

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

typedef uint8_t byte;

/// @typedef int ser_handle_t
/// @brief serial handle
//typedef int ser_handle_t;

/// @typedef enum ser_parity_t
/// @brief serial parity values
typedef enum{
    PAR_N='N',
    PAR_O='O',
    PAR_E='E'
}ser_parity_t;

/// @typedef enum ser_flow_t
/// @brief serial flow control values
typedef enum{
    FLOW_N='N',
    FLOW_H='H',
    FLOW_X='X'
}ser_flow_t;

/// @typedef enum ser_csize_t
/// @brief serial character size values
typedef enum{
    CSZ_5=5,
    CSZ_6=6,
    CSZ_7=7,
    CSZ_8=8
}ser_csize_t;

/// @typedef enum ser_cstop_t
/// @brief serial stop bit values
typedef enum{
    CSB_0=0,
    CSB_1=1,
    CSB_2=2,
}ser_cstop_t;

/// @typedef enum ser_speed_t
/// @brief serial IO speed values
typedef enum{
    BPS_INVALID=-1,
    BPS_4800=4800,
    BPS_9600=9600,
    BPS_19200=19200,
    BPS_38400=38400
}ser_speed_t;

/// @def SER_SPEED_DFL
/// @brief default serial speed
#define SER_SPEED_DFL 9600
/// @def SER_CSIZE_DFL
/// @brief default character size
#define SER_CSIZE_DFL CSZ_8
/// @def SER_CSTOP_DFL
/// @brief default stop bits
#define SER_CSTOP_DFL CSB_1
/// @def SER_PAR_DFL
/// @brief default parity
#define SER_PAR_DFL PAR_N
/// @def SER_FLOW_DFL
/// @brief default flow control
#define SER_FLOW_DFL FLOW_N

/// @def MSEC2NSEC(ms)
/// @brief convert msec to nsec (long int)
#define MSEC2NSEC(ms) ((long)ms*1000000L)

/// @def WSER_TEST_SET(f,c,i)
/// @brief test c and set bit i of f 1 if true, 0 if false
#define WSER_TEST_SET(f,c,i) (f |= ( (c) ? (0<<i) : (1<<i) ) )

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
#define TERM_T

#elif defined(__WIN32) || defined(_WIN64)
#define TERM_T HANDLE
/// @typedef struct serial_res_s serial_res_t
/// @brief serial port resource structure
typedef struct wser_s{
    /// @var wser_s::device
    /// @brief serial device path
    char *device;
    /// @var wser_s::speed
    /// @brief serial IO speed
    int speed;
    /// @var wser_s::csize
    /// @brief character size
    int csize;
    /// @var wser_s::cstop
    /// @brief character stop bits
    int cstop;
    /// @var wser_s::par
    /// @brief parity (enum)
    ser_parity_t par;
    /// @var wser_s::flow
    /// @brief flow control (enum)
    ser_flow_t flow;
    /// @var wser_s::fd
    /// @brief serial device file descriptor
    int fd;
    /// @var wser_s::term
    /// @brief terminal control structure
    TERM_T term;
    /// @var wser_s::oterm
    /// @brief terminal saved settings
    TERM_T oterm;
    /// @var wser_s::wt_msec
    /// @brief write inter-byte delay (msec)
    int wt_msec;
}wser_t;

#elif defined(__unix__) || defined(__APPLE__) || defined(__CYGWIN__)
#define TERM_T struct termios
/// @typedef struct serial_res_s serial_res_t
/// @brief serial port resource structure
typedef struct wser_s{
    /// @var wser_s::device
    /// @brief serial device path
    char *device;
    /// @var wser_s::speed
    /// @brief serial IO speed
    int speed;
    /// @var wser_s::csize
    /// @brief character size
    int csize;
    /// @var wser_s::cstop
    /// @brief character stop bits
    int cstop;
    /// @var wser_s::par
    /// @brief parity (enum)
    ser_parity_t par;
    /// @var wser_s::flow
    /// @brief flow control (enum)
    ser_flow_t flow;
    /// @var wser_s::fd
    /// @brief serial device file descriptor
    int fd;
    /// @var wser_s::term
    /// @brief terminal control structure
    TERM_T term;
    /// @var wser_s::oterm
    /// @brief terminal saved settings
    TERM_T oterm;
    /// @var wser_s::wt_msec
    /// @brief write inter-byte delay (msec)
    int wt_msec;
}wser_t;
#endif // platform-specific definitions

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

#ifdef __cplusplus
extern "C" {
#endif

    /// @fn wser_t *wser_new(const char *device)
    /// @brief create serial resource instance.
    /// caller must free using wser_destroy.
    /// @param[in] device serial device path
    /// @return instance pointer on success, NULL otherwise
    wser_t *wser_new(const char *device);

    /// @fn void wser_destroy(wser_t **pself)
    /// @brief release wser_t resources
    /// @param[in] pself pointer to instance pointer
    /// @return none
    void wser_destroy(wser_t **pself);

    /// @fn int wser_show(wser_t *self, bool verbose, int indent)
    /// @brief output formatted summary of instance to console
    /// @param[in] self instance
    /// @param[in] verbose more output, where supported
    /// @param[in] indent space to indent output
    /// @return bytes output on success, -1 otherwise
    int wser_show(wser_t *self, bool verbose, int indent);

    /// @fn int wser_connect(wser_t *self, int speed, int csize, ser_parity_t parity, int cstop, ser_flow_t flow)
    /// @brief establish serial connection
    /// @param[in] self instance
    /// @param[in] speed serial IO speed
    /// @param[in] csize character size (bits)
    /// @param[in] parity parity setting
    /// @param[in] cstop stop bits
    /// @param[in] flow flow control
    /// @return 0 on success, -1 otherwise
    int wser_connect(wser_t *self, int speed, int csize, ser_parity_t parity, int cstop, ser_flow_t flow);

    /// @fn int wser_t *wser_set_device(wser_t *self, const char *device);
    /// @brief change serial device (does not affect connection status)
    /// @param[in] self instance
    /// @param[in] device serial device path
    /// @return 0 on success, -1 otherwise
    int wser_set_device(wser_t *self, const char *device);

    /// @fn int wser_disconnect(wser_t *self)
    /// @brief close serial connection, restore terminal
    /// @param[in] self instance
    /// @return 0 on success, -1 otherwise
    int wser_disconnect(wser_t *self);

    /// @fn int wser_set_wt(wser_t *self, int wt_msec)
    /// @brief set write inter-byte delay (msec)
    /// used for all subsequent writes (no delay if <= 0)
    /// @param[in] self instance
    /// @param[in] wt_msec delay (msec)
    /// @return 0 on success, -1 otherwise
    int wser_set_wt(wser_t *self, int wt_msec);

    /// @fn int32_t wser_write(wser_t *self, char *msg, size_t len)
    /// @brief write bytes to serial port, with optional inter-character delay
    /// @param[in] self instance
    /// @param[in] dest destination buffer
    /// @param[in] len number of bytes to read
    /// @param[out] br bytes read (if non-NULL)
    /// @return bytes written on success, -1 otherwise
    int wser_read(wser_t *self, char *dest, size_t len, uint32_t *br);

    /// @fn int32_t wser_write(wser_t *self, char *msg, size_t len)
    /// @brief write bytes to serial port, with optional inter-character delay
    /// @param[in] self instance
    /// @param[in] src content to send
    /// @param[in] len number of bytes to write
    /// @param[out] bw bytes written (if non-NULL)
    /// @return bytes written on success, -1 otherwise
    int wser_write(wser_t *self, char *src, size_t len, uint32_t *bw);

    /// @fn int32_t wser_read_until(wser_t *self, char *term, char *dest, size_t len, int rt_sec)
    /// @brief read and optionally store input until buffer full, len characters read, timeout or byte sequence match
    /// @param[in] self instance
    /// @param[in] term terminating sequence (NULL if none)
    /// @param[in] dest input buffer (NULL if none)
    /// @param[in] len dest len (<=0 if none)
    /// @param[in] rt_sec timeout (seconds)
    /// @return bytes read on success, -1 otherwise
    int32_t wser_read_until(wser_t *self, char *term, char *dest, size_t len, int rt_sec);

    /// @fn int32_t wser_write_read(wser_t *self, char *src, size_t olen, char *dest, size_t ilen, char *term, int wt_msec, int rt_sec)
    /// @brief write bytes to serial port and optionally read response
    /// @param[in] self instance
    /// @param[in] src output content buffer (required)
    /// @param[in] olen output content length (bytes)
    /// @param[in] dest input content buffer (NULL if unused)
    /// @param[in] ilen input content length (bytes, 0 if unused)
    /// @param[in] term input termination sequence (NULL if unused)
    /// @param[in] rt_sec read timeout (<0 to block until term match or buffer full)
    /// @return bytes read on success, -1 otherwise
    int32_t wser_write_read(wser_t *self, char *src, size_t olen, char *dest, size_t ilen, char *term, int rt_sec);

    /// @fn int32_t wser_write_read_ln(wser_t *self, char *src, size_t olen, char *dest, size_t ilen, char *term, int wt_msec, int rt_sec)
    /// @brief wrapper for wser_write_read (appends LINEEND automatically)
    /// @param[in] self instance
    /// @param[in] src output content buffer (required)
    /// @param[in] dest input content buffer (NULL if unused)
    /// @param[in] ilen input content length (bytes, 0 if unused)
    /// @param[in] term input termination sequence (NULL if unused)
    /// @param[in] rt_sec read timeout (<0 to block until term match or buffer full)
    /// @return bytes read on success, -1 otherwise
    int32_t wser_write_read_ln(wser_t *self, char *src, size_t olen, char *dest,   size_t ilen, char *term, int rt_sec);

    /// @fn int32_t wser_write_read_ln(wser_t *self, char *src, size_t olen, char *dest, size_t ilen, char *term, int wt_msec, int rt_sec)
    /// @brief wrapper for wser_write_read (assumes src is NULL terminated string)
    /// @param[in] self instance
    /// @param[in] src output content buffer (required)
    /// @param[in] dest input content buffer (NULL if unused)
    /// @param[in] ilen input content length (bytes, 0 if unused)
    /// @param[in] term input termination sequence (NULL if unused)
    /// @param[in] rt_sec read timeout (<0 to block until term match or buffer full)
    /// @return bytes read on success, -1 otherwise
    int32_t wser_write_read_str(wser_t *self, char *src, char *dest, size_t ilen, char *term, int rt_sec);

    /// @fn int32_t wser_drain_rx_base(wser_t *self, int retries, uint32_t delay_msec, int rt_sec)
    /// @brief read and discard characters from serial input using optional delays, retries and timeout
    /// @param[in] self instance
    /// @param[in] retries character read retries (<=0 for none)
    /// @param[in] delay_msec (msec, delay between retries, 0 for none)
    /// @param[in] rt_sec timeout (sec, >=0 )
    /// @return characters read on success, -1 otherwise
    int32_t wser_drain_rx_base(wser_t *self, int retries, uint32_t delay_msec, time_t rt_sec);

    /// @fn int32_t wser_wdrain_rx(wser_t *self, int rt_sec)
    /// @brief wrapper for wser_drain_rx_base using no retries or delay
    /// @param[in] self instance
    /// @param[in] rt_sec timeout (sec, >=0 )
    /// @return characters read on success, -1 otherwise
    int32_t wser_drain_rx(wser_t *self, time_t rt_sec);

    /// @fn void wser_signal_int(bool enable)
    /// @brief set global variable that may be used to break out
    /// of blocking loops
    /// @param[in] set true to break, false to reset
    /// @return none
    void wser_signal_int(bool enable);

#ifdef __cplusplus
}
#endif

#endif // THIS_FILE
