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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>     // memset()
#include <stdint.h>     // uint8_t, ...
#include <fcntl.h>      // open()
#include <unistd.h>     // read()
#include <libgen.h>     // basename
#include <inttypes.h>   // PRIx64+
#include <signal.h>     // signal
#include <time.h>       // nanosleep, timespec
#if defined (_WIN32) || defined (_WIN64)
#include <conio.h>      // kbhit()
#endif

#include "common.h"

extern void tst_display_update(struct tst_session* sess);

char *app_name = NULL;
int running = 0;

static void help();
void handle_signal(int sig);

int main(int argc, char *argv[])
{
    int ret = 0;
    int dev_idx = 0;
    int station_max_opt = 255;
    int station_opt = 0;
    int bitrate_opt = 1;
    int value = 0;
    uint32_t tr_opt = 0;
    uint32_t ts_opt = 0;
    int cycle_ustime_opt = 100000; /* 100ms */
    struct timespec rqtp = {0};
    struct timespec rmtp = {0};
    struct tst_session sess;

    app_name = basename(argv[0]);

#if defined (_WIN32) || defined (_WIN64)
    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
    enable_console_colorization();
#else
    uint8_t buff = 0;
#endif

    /* Try to process all command line arguments */
    while ((value = getopt(argc, argv, "i:n:b:r:s:m:c:lhv")) != -1) {
        switch (value) {
            case 'i':
                sscanf(optarg, "%d", &dev_idx);
                break;
            case 'n':
                sscanf(optarg, "%d", &station_opt);
                if ((station_opt < 0) || (station_opt > 255)) {
                    help();
                    return EXIT_FAILURE;
                }
                break;
            case 'b':
                sscanf(optarg, "%d", &bitrate_opt);
                if (bitrate_opt >= (_PWRFIP_BITRATE_MAX - 1)) {
                    help();
                    return EXIT_FAILURE;
                }
                break;
            case 'r':
                sscanf(optarg, "%u", &tr_opt);
                break;
            case 's':
                sscanf(optarg, "%u", &ts_opt);
                break;
            case 'm':
                sscanf(optarg, "%d", &station_max_opt);
                if ((station_max_opt < 2) || (station_max_opt > 255)) {
                    help();
                    return EXIT_FAILURE;
                }
                break;
            case 'c':
                sscanf(optarg, "%d", &cycle_ustime_opt);
                if ((cycle_ustime_opt < 2000) || (cycle_ustime_opt > 500000)) {
                    help();
                    return EXIT_FAILURE;
                }
                break;
            case 'l':
                dev_show_all();
                return EXIT_SUCCESS;
            case 'h':
                help();
                return EXIT_SUCCESS;
            case 'v':
                printf("%s %s\n", app_name, APP_VERSION);
                return EXIT_SUCCESS;
            case '?':
            default:
                help();
                return EXIT_FAILURE;
        }
    }

    if (station_opt >= station_max_opt) {
        _print(app, err,
            "node address (-n %d) exceeds the maximum address (-m %d)\n",
            station_opt, station_max_opt);
        help();
        return EXIT_SUCCESS;
    }

    signal(SIGINT, handle_signal);

    /* pwrfip - initialization [mandatory] */
    if (pwrfip_init()) {
        _print(app, err, "cannot init pwrfip library: %s\n",
            pwrfip_strerror(errno));
        return -1;
    }

    /* exoligent - fip device opening */
    memset(&sess, 0, sizeof(struct tst_session));
    if ((ret = dev_open(dev_idx, &sess)))
        goto fail1;

    sess.station_number = station_opt;
    sess.station_bitrate = bitrate_opt;
    sess.station_tr_ustime = tr_opt;
    sess.station_ts_ustime = ts_opt;
    sess.station_max_count = station_max_opt;
    sess.ba_cycle_ustime = cycle_ustime_opt;
    if (tst_session_start(&sess) < 0) {
        _print(app, err, "impossible to start fip station %d.\n", station_opt);
        goto fail2;
    }

    _print(app, info, "app started. press enter to close...\n");

#if defined(__linux__) || defined(__QNX__)
    /* set stdin non-blocking */
    fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
#endif

    rqtp.tv_sec = 0;
    rqtp.tv_nsec = 500000000; /* 500ms */

    /* main loop */
    running = 1;
    while (running) {
        /* wait for a pressed key */
#if defined (_WIN32) || defined (_WIN64)
        if (kbhit())
#else
        if (read(STDIN_FILENO, &buff, 1) == 1)
#endif
        {
            running = 0;
        }

        tst_display_update(&sess);
        fflush(stdout);

        ret = nanosleep(&rqtp, &rmtp);
    }

    if (tst_session_stop(&sess))
        _print(app, err, "impossible to stop fip station %d.\n", station_opt);

fail2:
    /* exoligent - fip device closure */
    if (dev_close(&sess))
        _print(app, err, "cannot close correctly the device.\n");
fail1:
    pwrfip_exit();
    _print(app, info, "app closed\n");
    return ret;
}

/**
 * \brief Callback function for handling signals.
 * \param sig identifier of signal
 */
void handle_signal(int sig)
{
    if (sig == SIGINT) {
        _print(app, info, "SIGINT signal received\n");
        running = 0;
        /* Reset signal handling to default behavior */
        signal(SIGINT, SIG_DFL);
    } else
        _print(app, warn, "signal received but not supported (sig=%d)\n", sig);
}

/**
 * \brief Print help for this application
 */
static void help()
{
    printf("\n");
    printf("Usage: %s [OPTION]...\n\n", app_name);
    printf("It tests FIP board performance with PowerFIP library.\n");
    printf("By default, if no option is added, the app opens the first PCI/PCIe device\n");
    printf("detected, and starts FIP node 0 (addr=0) [-n 0] with bitrate sets\n");
    printf("to 1Mbps [-b 1].\n\n");
    printf("Options:\n");
    printf("    -i   device index [default=0 - the first one available]\n");
    printf("    -n   FIP node address to start [default=0]\n");
    printf("    -b   FIP bitrate [default=1]\n");
    printf("         0: 31.25Kbps\n");
    printf("         1: 1Mbps\n");
    printf("         2: 2.5Mbps\n");
    printf("         3: 5Mbps\n");
    printf("         4: 12.5Mbps\n");
    printf("         5: 25Mbps\n");
    printf("    -r   FIP turn-around time in us [default=0]\n");
    printf("         default TR times:\n");
    printf("           @31.25Kbps: 424us\n");
    printf("           @1Mbps    : 30us\n");
    printf("           @2.5Mbps  : 30us\n");
    printf("           @5Mbps    : 32us\n");
    printf("           @12.5Mbps : 32us\n");
    printf("           @25Mbps   : 32us\n");
    printf("    -s   FIP silence time in us [default=0]\n");
    printf("         default TS times:\n");
    printf("           @31.25Kbps: 4096us\n");
    printf("           @1Mbps    : 150us\n");
    printf("           @2.5Mbps  : 96us\n");
    printf("           @5Mbps    : 92us\n");
    printf("           @12.5Mbps : 92us\n");
    printf("           @25Mbps   : 92us\n");
    printf("    -m   max FIP nodes count on the network [default=255]\n");
    printf("    -c   FIP cycle time in microseconds [min=2000,max=500000] [default=100000]\n");
    printf("    -l   list the FIP boards present on the host machine\n");
    printf("    -h   show this help and exit\n");
    printf("    -v   show version and exit\n");
    printf("\n");
    printf("Examples: \n");
    printf("    %s -i 1 -n 0 -b 1 -r 30 -s 150 -c 10000 -m 2\n", app_name);
    printf("\n");
}