/**
 * transmitter.c — OFDM-передатчик с RS(255,223) FEC для PlutoSDR
 *
 * ОПТИМАЛЬНАЯ КОМАНДА ЗАПУСКА:
 * cat test.bin | ./transmitter -f 1265000000 -r 3840000 -b 2000000 -g -20 -a 0.3 -p 0 -c rs8
 */

#include "common.h"

// Параметры передатчика по умолчанию
#define DEFAULT_GAIN     -30.0         // Усиление передатчика: -30 дБ
#define DEFAULT_AMP      0.2f          // Амплитуда сигнала
#define DEFAULT_PAUSE    2000          // Пауза между кадрами: 2000 мкс
#define BUF_SAMPLES      16384         // Размер буфера в отсчётах
#define MAX_PAYLOAD      1024          // Максимальный размер полезной нагрузки

// Внешняя переменная флага завершения (определена в common.c)
extern volatile sig_atomic_t stop;

int main(int argc, char *argv[]) {
    // Инициализация параметров
    long long freq = DEFAULT_FREQ, rate = DEFAULT_RATE, bw = DEFAULT_BW;
    double gain = DEFAULT_GAIN;
    float amp = DEFAULT_AMP;
    const char *uri = TX_URI_DEFAULT;
    int pause_us = DEFAULT_PAUSE, use_fec = 1;

    // Обработка аргументов командной строки
    int opt;
    while ((opt = getopt(argc, argv, "f:r:b:g:a:p:u:c:h")) != -1) {
        switch (opt) {
            case 'f': freq = atoll(optarg); break;
            case 'r': rate = atoll(optarg); break;
            case 'b': bw = atoll(optarg); break;
            case 'g': gain = atof(optarg); break;
            case 'a': amp = atof(optarg); break;
            case 'p': pause_us = atoi(optarg); break;
            case 'u': uri = optarg; break;
            case 'c': use_fec = strcmp(optarg, "none") ? 1 : 0; break;
            case 'h':
                print_usage(argv[0], "ПЕРЕДАТЧИК");
                fprintf(stderr, "\nСПЕЦИФИЧНЫЕ ОПЦИИ TX:\n");
                fprintf(stderr, "  -g УСИЛЕНИЕ  Усиление в дБ (по умолчанию: %.0f)\n", DEFAULT_GAIN);
                fprintf(stderr, "  -a АМПЛИТ.   Амплитуда сигнала (по умолчанию: %.2f)\n", DEFAULT_AMP);
                fprintf(stderr, "  -p ПАУЗА     Пауза между кадрами в мкс (по умолчанию: %d)\n", DEFAULT_PAUSE);
                fprintf(stderr, "\nОПТИМАЛЬНЫЙ ЗАПУСК:\n");
                fprintf(stderr, "  cat test.bin | %s -f %lld -r %lld -b %lld -c rs8 > /dev/null\n",
                        argv[0], DEFAULT_FREQ, DEFAULT_RATE, DEFAULT_BW);
                return 0;
            default: return 1;
        }
    }

    // Настройка обработчиков сигналов
    setup_signal_handlers();

    // Вывод конфигурации
    print_config("ПЕРЕДАТЧИК", freq, rate, bw, use_fec);
    fprintf(stderr, "Усиление:    %.0f дБ\n", gain);
    fprintf(stderr, "Амплитуда:   %.2f\n", amp);
    fprintf(stderr, "Пауза:       %d мкс\n", pause_us);
    fprintf(stderr, "URI:         %s\n", uri);
    fprintf(stderr, "═══════════════════════════════════════════════════\n");

    // Инициализация PlutoSDR
    struct iio_context *ctx = NULL;
    struct iio_device *phy = NULL, *tx = NULL;
    if (pluto_init(uri, &ctx, &phy, &tx, 1) != 0) return 1;

    // Настройка радиотракта
    pluto_configure(phy, freq, rate, bw, gain, 1);

    // Включение каналов передатчика
    iio_channel_enable(iio_device_find_channel(tx, "voltage0", true));
    iio_channel_enable(iio_device_find_channel(tx, "voltage1", true));

    // Создание буфера передачи
    struct iio_buffer *buf = iio_device_create_buffer(tx, BUF_SAMPLES, false);
    if (!buf) {
        LOG_ERROR("Ошибка создания буфера передачи");
        iio_context_destroy(ctx);
        return 1;
    }

    // Инициализация FEC и OFDM
    fec enc = use_fec ? fec_create(LIQUID_FEC_RS_M8, NULL) : NULL;

    ofdmflexframegenprops_s fgp;
    ofdmflexframegenprops_init_default(&fgp);
    fgp.check = LIQUID_CRC_32;
    ofdmflexframegen fg = ofdmflexframegen_create(OFDM_M, OFDM_CP, OFDM_TAPER, NULL, &fgp);

    // Счётчики статистики
    uint64_t frames = 0, bytes_tx = 0;
    uint32_t seq = 0;
    time_t last = time(NULL);

    fprintf(stderr, " Передатчик готов. Ожидание данных...\n\n");

    // Основной цикл передачи
    while (!stop) {
        uint8_t raw[MAX_PAYLOAD];
        size_t n = fread(raw, 1, MAX_PAYLOAD, stdin);
        if (!n) {
            if (feof(stdin) || stop) break;
            usleep(10000);
            continue;
        }

        uint8_t *tx_payload = NULL;
        size_t tx_len = 0;
        uint8_t hdr[HDR_SIZE] = {0};

        // Кодирование данных
        if (enc) {
            size_t total_blocks = (n + RS_DATA - 1) / RS_DATA;
            tx_len = total_blocks * RS_ENC;
            tx_payload = malloc(tx_len);

            for (size_t b = 0; b < total_blocks; b++) {
                uint8_t blk[RS_DATA] = {0};
                size_t bytes = (b == total_blocks - 1) ? n - b * RS_DATA : RS_DATA;
                memcpy(blk, raw + b * RS_DATA, bytes);
                fec_encode(enc, RS_DATA, blk, tx_payload + b * RS_ENC);
            }

            // Формирование заголовка
            hdr[0] = HDR_SIGNATURE_0; hdr[1] = HDR_SIGNATURE_1;
            hdr[2] = (seq >> 24) & 0xFF; hdr[3] = (seq >> 16) & 0xFF;
            hdr[4] = (seq >> 8) & 0xFF;  hdr[5] = seq & 0xFF;
            hdr[6] = (n >> 8) & 0xFF;    hdr[7] = n & 0xFF;
            hdr[8] = (total_blocks >> 8) & 0xFF; hdr[9] = total_blocks & 0xFF;
            hdr[10] = n % RS_DATA;       hdr[11] = 0;
        } else {
            tx_len = n;
            tx_payload = malloc(tx_len);
            memcpy(tx_payload, raw, n);

            hdr[0] = HDR_SIGNATURE_0; hdr[1] = HDR_SIGNATURE_1;
            hdr[2] = (seq >> 24) & 0xFF; hdr[3] = (seq >> 16) & 0xFF;
            hdr[4] = (seq >> 8) & 0xFF;  hdr[5] = seq & 0xFF;
            hdr[6] = (n >> 8) & 0xFF;    hdr[7] = n & 0xFF;
        }

        // Сборка OFDM-кадра
        ofdmflexframegen_assemble(fg, hdr, tx_payload, tx_len);
        free(tx_payload);

        // Формирование выходного сигнала
        int16_t *out = (int16_t *)iio_buffer_start(buf);
        memset(out, 0, BUF_SAMPLES * 2 * sizeof(int16_t));
        int idx = 0, done = 0;
        float complex symb[OFDM_M + OFDM_CP];

        while (!done && (idx + OFDM_M + OFDM_CP) <= BUF_SAMPLES) {
            done = ofdmflexframegen_write(fg, symb, OFDM_M + OFDM_CP);
            for (int i = 0; i < OFDM_M + OFDM_CP; i++) {
                out[2 * idx] = (int16_t)(crealf(symb[i]) * 32767 * amp);
                out[2 * idx + 1] = (int16_t)(cimagf(symb[i]) * 32767 * amp);
                idx++;
            }
        }
        if (idx > 0) iio_buffer_push(buf);

        // Обновление статистики
        frames++; bytes_tx += n; seq++;
        if (time(NULL) > last) {
            fprintf(stderr, "\r📤 Передано: %llu кадров, %llu байт",
                    (unsigned long long)frames, (unsigned long long)bytes_tx);
            fflush(stderr);
            last = time(NULL);
        }
        if (pause_us > 0) usleep(pause_us);
    }

    fprintf(stderr, "\nПередача завершена: %llu кадров, %llu байт\n",
            (unsigned long long)frames, (unsigned long long)bytes_tx);

    // Освобождение ресурсов
    if (enc) fec_destroy(enc);
    ofdmflexframegen_destroy(fg);
    iio_buffer_destroy(buf);
    iio_context_destroy(ctx);
    return 0;
}