/*
 * Copyright (C) 2022 EXOLIGENT (www.exoligent.com)
 * Author: Maxime Coroyer <maxime.coroyer@exoligent.com>
 *
 * Description:
 * Modbus <-> FIP/WorldFIP gateway
 */

#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 "gw.h"

char *app_name = NULL;
int running = 0;

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

int main(int argc, char *argv[])
{
    int ret = 0;
    int dev_idx = 1;
    int value = 0;
    int display_delay = 6; /* 3s (6*500ms) */
    char config_path[255];
    struct timespec rqtp = {0};
    struct timespec rmtp = {0};
    struct gateway gw;

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

    /* default config path */
    memset(config_path, 0, 255 * sizeof(char));
    sprintf(config_path, "./config/performance/sta1.json");

    app_name = basename(argv[0]);

    /* Try to process all command line arguments */
    while ((value = getopt(argc, argv, "i:f:lhv")) != -1) {
        switch (value) {
            case 'i':
                sscanf(optarg, "%d", &dev_idx);
                break;
            case 'f':
                memset(config_path, 0, 255 * sizeof(char));
                sscanf(optarg, "%s", config_path);
                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;
        }
    }

    signal(SIGINT, handle_signal);

    memset(&gw, 0, sizeof(struct gateway));

    gw_cfg_parse(config_path, &gw);

    /* fip starting ... */
    gw.fip.dev_idx = dev_idx;
    if (fip_start(&gw) < 0)
        goto end1;

    /* modbus starting ... */
    if (mbus_srv_start(&gw) < 0)
        goto end2;

#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 */
    _usleep(500);
    _print(app, info,
        "[ok] mb-fip gateway is active. press enter to close...\n");

    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;
        }

        if (display_delay)
            display_delay--;
        else
            gw_display_update(&gw);

        ret = nanosleep(&rqtp, &rmtp);
    }
    printf("\n");
    ret = 0;

end2:
    ret |= mbus_srv_stop(&gw);
    if (ret)
        _print(mbus, err, "[failed] modbus server stop\n");
    else
        _print(mbus, info, "[ok] modbus server stop\n");

    ret |= fip_stop(&gw);
    if (ret)
        _print(fip, err, "[failed] fip node stop: %s\n",
            pwrfip_strerror(errno));
    else
        _print(fip, info, "[ok] fip node stop\n");

end1:
    if (!ret)
        _print(app, info, "[ok] mb-fip gateway is stopped\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("Modbus <-> FIP/WorldFIP gateway.\n");
    printf("By default, if no option is added, the app opens the first FIP boards detected\n\n");
    printf("Options:\n");
    printf("    -i   device index [default=1]\n");
    printf("    -f   configuration path [default=./config/performance/sta1.json]\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 -f \"./config/performance/sta1.json\"\n", app_name);
    printf("\n");
}