#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
    #include <locale.h>
#endif

#include "ltr/include/ltr42api.h"

#include "getopt.h"


/*================================================================================================*/
struct Options {
    int slot;
    int autorun_en;
    TLTR_CARD_START_MODE start_mode;
    int storeconfig_on;
    int ack_en;
    struct {
        int en;
        unsigned val;
    } out_demo;
    struct {
        int second;
        int start;
    } tmarks_demo_en;
};


/*================================================================================================*/
static const int err_any = LTR_OK + 1;
static const int err_none = LTR_OK;
TLTR42 hltr42;


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


/*================================================================================================*/
/*------------------------------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
    struct Options opts;
    WORD OutputWord;
    INT err = err_none;                     /* Error code */
    int ltr42_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 = LTR42_Init(&hltr42)) != LTR_OK) {
        fprintf(stderr, "Не удалось проинициализировать дескриптор модуля. Ошибка %d (%s)\n",
            err, LTR42_GetErrorString(err));
        goto cleanup;
    }
    printf("LTR42_Init()->OK\n");

    /* Открываем интерф. канал связи с модулем. Сетевой адрес и номер порта - по умолчанию
     * Серийный номер первого найденного модуля;
     * Номер посадочного места - 1;
     */
    if ((err = LTR42_Open(&hltr42, 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, LTR42_GetErrorString(err));
            goto cleanup;
        }
    } else {
        printf("LTR42_Open()->OK\n");
    }
    ltr42_isopened = 1;

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

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

    if (opts.out_demo.en) {
        /* Выводим слово в порты вывода. */
        OutputWord = opts.out_demo.val;
        printf("Запишем в порты ввода-вывода слово %0#4X\n", OutputWord);
        if ((err = LTR42_WritePortSaved(&hltr42, OutputWord)) != LTR_OK) {
            fprintf(stderr, "Не удалось записать слово в порт вывода. Ошибка %d (%s)\n.", err,
                LTR42_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR42_WritePort()->OK\n");
    }

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

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

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

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

    if (opts.storeconfig_on) {
        TLTR_SETTINGS cfg;
        TLTR h_ltr;

        printf("Сохранение конфигурации модуля и крейта\n");
        if ((err = LTR42_StoreConfig(&hltr42, opts.start_mode)) != LTR_OK) {
            fprintf(stderr, "Не удалось сохранить конфигурацию модуля. Ошибка %d (%s)\n", err,
                LTR42_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR42_StoreConfig()->OK\n");

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

        if ((err = LTR_OpenCrate(&h_ltr, hltr42.Channel.saddr, hltr42.Channel.sport,
                                 LTR_CRATE_IFACE_UNKNOWN, hltr42.Channel.csn)) != LTR_OK) {
            fprintf(stderr, "Не удалось открыть соединение с крейтом. Ошибка %d (%s).\n", err,
                LTR_GetErrorString(err));
            goto cleanup;
        }
        printf("LTR_OpenCrate()->OK\n");

        cfg.size = sizeof(TLTR_SETTINGS);
        cfg.autorun_ison = opts.autorun_en;
        if ((err = LTR_PutSettings(&h_ltr, &cfg)) != LTR_OK) {
            fprintf(stderr, "Не удалось сохранить конфигурацию крейта. Ошибка %d (%s).\n", err,
                LTR_GetErrorString(err));
            LTR_Close(&h_ltr);
            goto cleanup;
        }
        printf("LTR_PutSettings()->OK\n");

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


cleanup:
    if (ltr42_isopened) {
        if ((err = LTR42_Close(&hltr42)) != LTR_OK) {
            fprintf(stderr, "Не удалось закрыть соединение с модулем. Ошибка %d (%s).\n", err,
                LTR42_GetErrorString(err));
        } else {
            printf("LTR42_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:RMCto:sS";

    /* Set default options values */
    opts->slot = LTR_CC_CHNUM_MODULE1;
    opts->autorun_en = 0;
    opts->start_mode = LTR_CARD_START_OFF;
    opts->storeconfig_on = 0;
    opts->ack_en = 1;
    opts->out_demo.en = 1;
    opts->out_demo.val = 0xB174;
    opts->tmarks_demo_en.second = 0;
    opts->tmarks_demo_en.start = 0;

    while ((optch = getopt_long(argc, argv, optstr, NULL, NULL)) != -1) {
        switch (optch) {
        case 'p':
            opts->slot = atoi(optarg);
            break;
        case 'R':
            opts->autorun_en = 1;
            break;
        case 'M':
            opts->start_mode = LTR_CARD_START_RUN;
            break;
        case 'C':
            opts->storeconfig_on = 1;
            break;
        case 't':
            opts->ack_en = 0;
            break;
        case 'o':
            opts->out_demo.en = 1;
            opts->out_demo.val = strtoul(optarg, NULL, 16);
            break;
        case 's':
            opts->tmarks_demo_en.second = 1;
            break;
        case 'S':
            opts->tmarks_demo_en.start = 1;
            break;
        default:
            return err_any;
        }
    }

    return err_none;
}
