#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#endif

#include "ltr/include/ltr41api.h"

#include "getopt.h"


/*================================================================================================*/
struct Options {
    int slot;
    struct {
        int second;
        int start;
    } tmarks_demo_en;
    unsigned update_inputs_ms;
};


/*================================================================================================*/
static void cb_abort(int sig);
static int parse_opts(int argc, char **argv, struct Options *opts);


/*================================================================================================*/
static const int err_any = LTR_OK + 1;
static const int err_none = LTR_OK;
char ErrorString[255];
TLTR41 hltr41;
static volatile int prog_aborted;


/*================================================================================================*/
/*------------------------------------------------------------------------------------------------*/
#ifndef _WIN32
static void cb_abort(int sig) {
    /* Обработчик сигнала завершения для Linux */
    prog_aborted = 1;
}
#endif

/*------------------------------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
    WORD input_word;
    struct Options opts;
    INT err = err_none;                     /* Error code */
    int ltr41_isopened = 0;
#ifdef _WIN32
    /* для вывода русских букв в консоль для ОС Windows*/
    setlocale(LC_ALL, "");
#endif

    if ((err = parse_opts(argc, argv, &opts)) != err_none) {
        fprintf(stderr, "Неверно заданные опции!\n");
        goto cleanup;
    }

    /* Инициализируем канал связи с модулем и структуру описания модуля */
    printf("Выполняем инициализацию модуля\n");

    if ((err = LTR41_Init(&hltr41)) != LTR_OK) {
        fprintf(stderr, "Не удалось проинициализировать дескриптор модуля. Ошибка %d (%s)\n",
            err, LTR41_GetErrorString(err));
        goto cleanup;
    }
    printf("LTR41_Init()->OK\n");

    /* Открываем интерф. канал связи с модулем. Сетевой адрес и номер порта - по умолчанию
     * Серийный номер первого найденного модуля;
     * Номер посадочного места - из командной строки;
     */
    if ((err = LTR41_Open(&hltr41, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT, "", opts.slot)) != LTR_OK) {
        if (err == LTR_WARNING_MODULE_IN_USE) {
            fprintf(stderr, "Предупреждение: соединение с модулем уже установлено.\n");
        } else {
            fprintf(stderr, "Не удалось открыть установить соединение с модулем. Ошибка %d (%s).\n",
                err, LTR41_GetErrorString(err));
            goto cleanup;
        }
    } else {
        printf("LTR41_Open()->OK\n");
    }
    ltr41_isopened = 1;

    printf("Имя модуля: %s\n", hltr41.ModuleInfo.Name);
    printf("Версия прошивки AVR: %s\n", hltr41.ModuleInfo.FirmwareVersion);
    printf("Дата создания прошивки AVR: %s\n", hltr41.ModuleInfo.FirmwareDate);
    printf("Серийный номер модуля: %s\n\n", hltr41.ModuleInfo.Serial);

    /* Производим заполнение полей структуры описания модуля требуемыми значениями */
    /* Конфигурация меток */
    hltr41.Marks.SecondMark_Mode = 1;       /* Секундная метка внутр. с трансляцией на выход */
    hltr41.Marks.StartMark_Mode = 0;        /* Метка СТАРТ внутренняя */
    /* Вызываем функцию конфигурации модуля */
    if ((err = LTR41_Config(&hltr41)) != LTR_OK) {
        fprintf(stderr, "Не удалось сконфигурировать модуль. Ошибка %d (%s)\n", err,
            LTR41_GetErrorString(err));
        goto cleanup;
    }
    printf("LTR41_Config()->OK\n");


    if (opts.update_inputs_ms > 0) {
        prog_aborted = 0;
#ifndef _WIN32
        struct sigaction sa;
        memset(&sa, 0, sizeof(sa));
        /* В ОС Linux устанавливаем свой обработчик на сигнал закрытия,
           чтобы завершить сбор корректно */
        sa.sa_handler = cb_abort;
        sigaction(SIGTERM, &sa, NULL);
        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGABRT, &sa, NULL);
#endif

        printf("Чтение входов запущено. Для останова нажмите CTRL-C.\n");
    } else {
        prog_aborted = 1;
    }

    do {
        /* Считаем слово с линий ввода */
        printf("Считываем слово с линий ввода\n");
        if ((err = LTR41_ReadPort(&hltr41, &input_word)) != LTR_OK) {
            fprintf(stderr, "Не удалось прочитать состояние линий ввода. Ошибка %d (%s).\n", err,
                LTR41_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR41_ReadPort()->OK\n");
        printf("Считанное слово: %0#4X\n", input_word);

        LTRAPI_SLEEP_MS(opts.update_inputs_ms);

#ifdef _WIN32
        /* проверка нажатия клавиши для выхода */
        if (_kbhit())
            prog_aborted = 1;
#endif
    } while (!prog_aborted);

    if (opts.tmarks_demo_en.second) {
        /* Запускаем генерацию секундных меток */
        printf("Запускаем генерацию секундных меток\n");
        if ((err = LTR41_StartSecondMark(&hltr41)) != LTR_OK) {
            fprintf(stderr, "Не удалось запустить генерацию секундных меток. Ошибка %d (%s)\n.",
                err, LTR41_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR41_StartSecondMarks()->OK\n");

        /* Подождем 3 секунды. После этого в сервере видно, что пришло 3 секундные метки */
        LTRAPI_SLEEP_MS(3000);

        /* Останавливаем генерацию секундных меток */
        printf("Останавливаем генерацию секундных меток\n");
        if ((err = LTR41_StopSecondMark(&hltr41)) != LTR_OK) {
            fprintf(stderr,
                "Не удалось остановить генерацию секундных меток. Ошибка %d (%s).\n", err,
                LTR41_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR41_StopSecondMark()->OK\n");
    }

    if (opts.tmarks_demo_en.start) {
        /* Сгенерируем метку СТАРТ */
        printf("Сгенерируем метку СТАРТ\n");
        if ((err = LTR41_MakeStartMark(&hltr41)) != LTR_OK) {
            fprintf(stderr, "Не удалось сгенерировать метку СТАРТ. Ошибка %d (%s).\n", err,
                LTR41_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR42_MakeStartMark()->OK\n");
    }


cleanup:
    if (ltr41_isopened) {
        if ((err = LTR41_Close(&hltr41)) != LTR_OK) {
            fprintf(stderr, "Не удалось закрыть соединение с модулем. Ошибка %d (%s).\n", err,
                LTR41_GetErrorString(err));
        } else {
            printf("LTR41_Close()->OK\n");
        }
    }

    return (err == err_none) ? EXIT_SUCCESS : EXIT_FAILURE;
}

/*------------------------------------------------------------------------------------------------*/
static int parse_opts(int argc, char **argv, struct Options *opts) {
    int optch;
    const char optstr[] = "p:sSi:";

    /* Set default options values */
    opts->slot = LTR_CC_CHNUM_MODULE1;
    opts->tmarks_demo_en.second = 0;
    opts->tmarks_demo_en.start = 0;
    opts->update_inputs_ms = 0;

    while ((optch = getopt_long(argc, argv, optstr, NULL, NULL)) != -1) {
        switch (optch) {
        case 'p':
            opts->slot = atoi(optarg);
            break;
        case 's':
            opts->tmarks_demo_en.second = 1;
            break;
        case 'S':
            opts->tmarks_demo_en.start = 1;
            break;
        case 'i':
            opts->update_inputs_ms = atoi(optarg) * 1000;
            break;
        default:
            return err_any;
        }
    }

    return err_none;
}
