/*
 * All Rights Reserved (C) 2021-2025
 * Exoligent SARL (Colombes, France)
 * Website: www.exoligent.com
 * Author : Maxime Coroyer <maxime.coroyer@exoligent.com>
 */

#ifndef __COMMON_H__
#define __COMMON_H__

#if defined(__QNX__)
#   include <sys/syspage.h> // SYSPAGE_ENTRY
#endif

#if defined (_WIN32) || defined (_WIN64)
#   include <windows.h>
/* Some MinGW/CYGWIN distributions don't define the macro: */
#   ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#       define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#   endif
#endif

#include <sys/time.h>   // gettimeofday

#include <stdio.h>
#include <stdlib.h>
#include <string.h>     // memset()
#include <stdint.h>     // uint8_t, ...
#include <inttypes.h>   // PRIu
#include <stdbool.h>    // bool
#include <pthread.h>    // pthread()
#include <unistd.h>     // usleep()
#include <stdarg.h>     // args

#include "libpowerfip.h"

#define APP_VERSION "1.4.0"

#define MAX_FA_BOARD       16
#define TST_BA_MCYCLE_US   300000
#define TST_CONS_VAR_COUNT 126
#define TST_PROD_VAR_COUNT 126
#define TST_VAR_COUNT (TST_CONS_VAR_COUNT + TST_PROD_VAR_COUNT)
#define TST_CONS_MSG_COUNT 1
#define TST_PROD_MSG_COUNT 1
#define TST_MSG_COUNT (TST_CONS_MSG_COUNT + TST_PROD_MSG_COUNT)
#define TST_SYNC_COUNT 1
#define TST_BA_PER_REQ_COUNT (TST_VAR_COUNT + TST_MSG_COUNT + TST_SYNC_COUNT)
#define TST_BA_WIND_COUNT 2
#define TST_BA_MCYCLE_CNT 2

#pragma pack(1) // 1 byte alignment
struct tst_measure {
    /* init measure: pwrfip_node_init() */
    uint64_t nb_init;
    uint64_t cumul_init_duration;
    uint64_t min_init_duration;
    uint64_t max_init_duration;
    /* start-up measure: pwrfip_node_start() */
    uint64_t nb_start;
    uint64_t cumul_start_duration;
    uint64_t min_start_duration;
    uint64_t max_start_duration;
    /* fip variable writing measure: pwrfip_var_write() */
    /* note: the idx+1 corresponds to the number of writing bytes */
    uint64_t nb_w_var_by_id[TST_PROD_VAR_COUNT];
    uint64_t cumul_w_var_by_id_duration[TST_PROD_VAR_COUNT];
    uint64_t min_w_var_by_id_duration[TST_PROD_VAR_COUNT];
    uint64_t max_w_var_by_id_duration[TST_PROD_VAR_COUNT];
    /* fip variable reading measure: pwrfip_var_read() */
    /* note: the idx+1 corresponds to the number of reading bytes */
    uint64_t nb_r_var_by_id[TST_CONS_VAR_COUNT];
    uint64_t cumul_r_var_by_id_duration[TST_CONS_VAR_COUNT];
    uint64_t min_r_var_by_id_duration[TST_CONS_VAR_COUNT];
    uint64_t max_r_var_by_id_duration[TST_CONS_VAR_COUNT];
    /* fip message writing measure */
    /* note: the idx+1 corresponds to the number of writing bytes */
    uint64_t nb_w_msg[256];
    uint64_t cumul_w_msg_duration[256];
    uint64_t min_w_msg_duration[256];
    uint64_t max_w_msg_duration[256];
    /* synchro callback measure */
    uint64_t nb_sync_cb;
    uint64_t cumul_sync_cb_duration;
    uint64_t min_sync_cb_duration;
    uint64_t max_sync_cb_duration;
    uint64_t min_sync_cb_last;
    uint64_t max_sync_cb_last;
    /* fip messages stats */
    uint64_t rx_msg_ok;
    uint64_t tx_msg_ok;
    uint64_t rx_msg_warn;
    uint64_t tx_msg_warn;
    /* fip variables stats */
    uint64_t r_var_ok;
    uint64_t w_var_ok;
    uint64_t r_var_warn;
    uint64_t w_var_warn;
    uint64_t r_var_pyld_err; /* bad payload */
    uint64_t r_var_inc_err; /* bad increment payload */
};

struct tst_node {
    struct pwrfip_node *n;
    /* aele configuration */
    struct pwrfip_aele *al;
    struct pwrfip_var *cons_varlist[TST_CONS_VAR_COUNT]; /* consumed varlist */
    struct pwrfip_var *prod_varlist[TST_PROD_VAR_COUNT]; /* produced varlist */
    struct pwrfip_msg *cons_msglist[TST_CONS_MSG_COUNT]; /* consumed msglist */
    struct pwrfip_msg *prod_msglist[TST_PROD_MSG_COUNT]; /* produced msglist */
    /* bus arbiter: macrocycle configuration */
    struct pwrfip_ba_mcycle *mc_list[TST_BA_MCYCLE_CNT];
    /* bus arbiter status */
    struct pwrfip_ba_status ba_status;
};

struct tst_session {
    /* pci device handle */
    struct pwrfip_dev *dev;
    uint8_t station_number;
    uint8_t station_bitrate;
    uint32_t station_tr_ustime;
    uint32_t station_ts_ustime;
    /* fip node instance (handles) */
    struct tst_node node;
    /* performance measurement */
    struct tst_measure stats;
};
#pragma pack()

extern int running;
extern bool dbg_coloriz;
extern const char *pwrfip_ba_state_str[_PWRFIP_BA_STATE_MAX];

/****************************************************************************************
 * FIP STATION FUNCTIONS
 ***************************************************************************************/
extern int tst_session_start(struct tst_session *sess);
extern int tst_session_stop(struct tst_session *sess);
extern int tst_node_report(struct tst_session *sess);
extern void tst_pwrfip_ba_sync_handler(struct pwrfip_node *node,
    struct pwrfip_var *var, struct pwrfip_event *evt);

/****************************************************************************************
 * FIP BOARDS FUNCTIONS
 ***************************************************************************************/
extern int dev_open(int dev_idx, struct tst_session *sess);
extern int dev_close(struct tst_session *sess);
extern void dev_show_all();

/****************************************************************************************
 * STATISTICS
 ***************************************************************************************/
extern void update_min_max(uint64_t current_value, uint64_t *min_value,
            uint64_t *max_value);
extern int _usleep(uint32_t usec);
#if defined(__QNX__) || defined(_WIN32) || defined(_WIN64)
extern int _gettime(uint64_t *when);
extern void update_stats(uint64_t *start, uint64_t *stop, uint64_t *cp,
    uint64_t *cumul, uint64_t *min, uint64_t *max);
#else
extern int _gettime(struct timeval *when);
extern void update_stats(struct timeval *start, struct timeval *stop,
    uint64_t *cp, uint64_t *cumul, uint64_t *min, uint64_t *max);
#endif
extern void print_stats(FILE *f, struct tst_session *sess);

/****************************************************************************************
 * TIME/DATE FUNCTIONS
 ***************************************************************************************/
#define DBG_PRINT_INFO 1
#define DBG_PRINT_WARN 1
#define DBG_PRINT_ERR  1
#define DBG_PRINT_EVT  1
#define DBG_DUMP_MEM   0
#define DGB_COLORIZ    1

typedef enum
{
    sta0 = 0,   // station0
    sta1,       // station1
    app,        // application (general)
    no_src      // unknown
} msg_src;

typedef enum
{
    info = 0,   // information
    warn,       // warning
    err,        // error
    evt,        // event (callback)
    no_type     // unknown
} msg_type;

extern char *get_date();
extern struct timespec timespec_diff(struct timespec start, struct timespec end);
extern struct timespec timespec_add_ns(struct timespec ref, long ns);
extern bool b_then_a(struct timespec a, struct timespec b);

#if defined(__QNX__)
extern uint64_t clock_diff_ns(uint64_t start_clk_cycl, uint64_t end_clk_cycl);
#endif

#if defined(__QNX__) || defined(_WIN32) || defined(_WIN64)
extern unsigned long get_elapsed_us(uint64_t end, uint64_t start);
#else
extern unsigned long get_elapsed_us(struct timeval end, struct timeval start);
#endif

/****************************************************************************************
 * CUSTOMIZED PRINT FUNCTION
 ***************************************************************************************/
extern int _print(msg_src src, msg_type type, const char *fmt, ...);

#if defined (_WIN32) || defined (_WIN64)
extern void enable_console_colorization(); /* enable console colorization for win */
#endif

#endif