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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>     // syslog
#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

#include "gw.h"

int running = 0;
static int reload = 0;
static char *app_name = NULL;
static char *flog_name = NULL;

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

int main(int argc, char *argv[])
{
    int ret = 0;
    int dev_idx = 0;
    int value = 0;
    char config_path[512];

#if defined (_WIN32) || defined (_WIN64)
    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
    enable_console_colorization();
#endif

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

    app_name = basename(argv[0]);
    reload = 0;

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

    /* open system log and write message to it */
    openlog(argv[0], LOG_PID | LOG_CONS, LOG_DAEMON);
    syslog(LOG_INFO, "Started OPCUA TSN <-> WorldFIP gateway");

    /* signals registration */
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);
    signal(SIGQUIT, handle_signal);
    signal(SIGHUP, handle_signal);

    /* try to open log file */
    if (flog_name) {
        flog = fopen(flog_name, "a+");
        if (flog)
            dbg_coloriz = false; /* disable colorization for external logs */
        else {
            syslog(LOG_ERR, "Cannot open log file: %s (error=%s)",
                flog_name, strerror(errno));
            flog = stdout;
        }
    } else
        flog = stdout;

restart:
    memset(&gw, 0, sizeof(struct gateway));
    if (gw_cfg_parse(config_path, &gw) < 0) {
        _print(app, err, "[failed] bad gateway configuration file\n");
        goto end1;
    }

    if (reload) {
        syslog(LOG_INFO, "Reloaded configuration file %s", config_path);
        reload = 0;
    } else {
        UA_LOCK_INIT(&gw.opc.mu); /* init opc mutex */
        syslog(LOG_INFO, "Loaded configuration file %s", config_path);
    }

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

    /* opcua starting ... */
    if (opc_pub_start(&gw) < 0)
        goto end2;

    /* main loop */
    /* TODO: wait a signal */
    _usleep(2000); //--!!
    _print(app, info,
        "[ok] ua-fip gateway is active. press Ctrl + C to exit.\n");

    gateway_task();
    ret = 0;

end2:
    ret |= opc_pub_stop(&gw);
    if (ret)
        _print(opc, err, "[failed] opcua publisher stop\n");
    else
        _print(opc, info, "[ok] opcua publisher 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] ua-fip gateway is stopped\n");

    if (reload)
        goto restart;

    /* close log file */
    if (flog != stdout)
        fclose(flog);

    /* close system log */
    syslog(LOG_INFO, "Stopped OPCUA TSN <-> WorldFIP gateway");
    closelog();

    /* free allocated memory */
    if (flog_name)
        free(flog_name);

    return ret;
}

int gateway_task()
{
    int err = 0;
    //int display_delay = 6; /* 3s (6*500ms) */
    struct timespec rqtp = {0};
    struct timespec rmtp = {0};

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

    running = 1;
    while (running) {

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

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

    return err;
}

/**
 * \brief Callback function for handling signals.
 * \param sig identifier of signal
 */
void handle_signal(int sig)
{
    if ((sig == SIGINT) ||
        (sig == SIGTERM) ||
        (sig == SIGQUIT) ||
        (sig == SIGHUP)) {

        switch (sig) {
            case SIGINT:  _print(app, evt, "SIGINT signal received\n"); break;
            case SIGTERM: _print(app, evt, "SIGTERM signal received\n"); break;
            case SIGQUIT: _print(app, evt, "SIGQUIT signal received\n"); break;
            default:      _print(app, evt, "SIGHUP signal received\n");
                          reload = 1;
            break;
        }
        running = 0;
        /* reset signal handling to default behavior */
        signal(sig, 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("OPC UA Pub/Sub <-> 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=0 - the first one available]\n");
    printf("    -f   gateway configuration file path [default=./config/performance/sta1.json]\n");
    printf("    -L   output log trace file [default=/dev/stdout]\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");
}