/*
 * Тест длинной полезной нагрузки IR (0x5E + строка, как ответ версии в Car).
 * Передача через DMA + TIM17 — как в машинке (Car/src/IR/IR.cpp, BoardTest/IR_DMA).
 * Авто-режим: чередование total длины кадра 24/25 байт.
 */
#include "IR_Decoder.h"
#include "IR_Encoder.h"
#include "TimerStatic.h"

/** 1 — TX через DMA (STM32G4). */
#ifndef LONGDATA_USE_DMA
#define LONGDATA_USE_DMA 0
#endif

#if LONGDATA_USE_DMA
#include <IrDmaTxStm32.h>
#endif

static constexpr uint16_t kIrDeviceAddr = 0;
static constexpr uint8_t kCmdVersion = 0x5E;
static constexpr uint32_t kSerialBaud = 115200;
static constexpr uint32_t kSendPeriodMs = 500;
static constexpr uint8_t kMaxPayload = bytePerPack;
static constexpr uint8_t kMaxParamBytes = kMaxPayload - 1;

static IR_Encoder enc(PIN_IR_ENC_FORWARD, kIrDeviceAddr, nullptr);
static HardwareTimer irTimer(TIM17);

#if LONGDATA_USE_DMA
namespace {
constexpr size_t kIrDmaStreams = 1;
constexpr uint16_t kIrDmaTxWordCount = 4096U;
constexpr size_t kIrDmaTxMaxGateRuns = 1024U;
static uint32_t s_irDmaWords[kIrDmaTxWordCount];
static IR_Encoder::IR_TxGateRun s_irGateRuns[kIrDmaTxMaxGateRuns];
} // namespace

static IrDmaTxStm32<kIrDmaStreams> dmaBackend;
static bool txBusy(void * /*ctx*/) { return dmaBackend.busy(); }
static bool txStart(void * /*ctx*/, IR_Encoder *e, const uint8_t *packet, uint8_t len) {
    return dmaBackend.start(e, packet, len);
}
#endif

static char s_paramAscii[kMaxParamBytes + 1];
static uint8_t s_irPayload[kMaxPayload];
static uint8_t s_irPayloadLen = 0;
static uint32_t s_lastSendMs = 0;
static bool s_sendLongerFrame = false;

// 24 байта total: msg(1)+addr(2)+addr(2)+data(17)+crc(2), где data=0x5E + 16 ASCII.
static const char kPayload16[] = "Car_v4.3.9_[12MH";
// 25 байт total: как выше, но data=0x5E + 17 ASCII.
static const char kPayload17[] = "Car_v4.3.9_[12MHz]_G491";

static void rebuildIrPayload() {
    s_irPayload[0] = kCmdVersion;
    size_t n = 0;
    while (n < sizeof(s_paramAscii) && s_paramAscii[n]) {
        ++n;
    }
    const size_t copyLen = (n > kMaxParamBytes) ? kMaxParamBytes : n;
    memcpy(s_irPayload + 1, s_paramAscii, copyLen);
    s_irPayloadLen = static_cast<uint8_t>(1 + copyLen);
}

static void setAlternatingPayload() {
    const char* src = s_sendLongerFrame ? kPayload17 : kPayload16;
    strncpy(s_paramAscii, src, kMaxParamBytes);
    s_paramAscii[kMaxParamBytes] = '\0';
    s_sendLongerFrame = !s_sendLongerFrame;
}

static void sendVersionPacket() {
    rebuildIrPayload();
    const IR_SendResult r = enc.sendData(IR_Broadcast, s_irPayload, s_irPayloadLen);
    Serial.print(F("TX 0x5E + "));
    Serial.print((unsigned)(s_irPayloadLen - 1));
    Serial.print(F(" B, ok="));
    Serial.print(r.success ? F("1") : F("0"));
    Serial.print(F(", t="));
    Serial.print(r.sendTimeMs);
    Serial.println(F(" ms"));
}

void setup() {
    Serial.begin(kSerialBaud);

    strncpy(s_paramAscii, kPayload16, kMaxParamBytes);
    s_paramAscii[kMaxParamBytes] = '\0';
    rebuildIrPayload();

    #if LONGDATA_USE_DMA
    // IR_Encoder::setCarrierMultiply(N); // до beginClockOnly; после смены — retuneCarrierClock()
    IR_Encoder::beginClockOnly(&irTimer);

    IrDmaTxStm32<kIrDmaStreams>::Config cfg;
    cfg.timer = &irTimer;
    cfg.streamCount = kIrDmaStreams;
    cfg.streams[0].instance = DMA1_Channel1;
    cfg.streams[0].irq = DMA1_Channel1_IRQn;
    cfg.streams[0].dmamuxRequest = DMA_REQUEST_TIM17_UP;
    cfg.streams[0].enc = &enc;
    cfg.streams[0].dmaWords = s_irDmaWords;
    cfg.streams[0].dmaWordCount = kIrDmaTxWordCount;
    cfg.streams[0].gateRuns = s_irGateRuns;
    cfg.streams[0].maxGateRuns = kIrDmaTxMaxGateRuns;

    if (!dmaBackend.begin(cfg)) {
        Serial.println(F("[IR_DMA] init FAILED"));
        return;
    }
    IR_Encoder::setExternalTxBackend(txStart, txBusy, nullptr);
    #elif LONGDATA_LEGACY_ISR
    IR_Encoder::begin(&irTimer, 1, TIM17_IRQn, 0);
    #else
    IR_Encoder::begin(&irTimer, 1, TIM17_IRQn, 0);
    #endif
    enc.enable();

    #if LONGDATA_USE_DMA
    Serial.println(F("longData: DMA TX alternating 24/25 bytes"));
    #elif LONGDATA_LEGACY_ISR
    Serial.println(F("longData: legacy ISR TX (pre-unified FSM) 24/25 bytes"));
    #else
    Serial.println(F("longData: ISR TX (unified FSM) alternating 24/25 bytes"));
    #endif
    Serial.print(F("Auto-period ms = "));
    Serial.println((unsigned long)kSendPeriodMs);
}

void loop() {
    #if LONGDATA_USE_DMA
    IR_Encoder::tick();
    #endif

    const uint32_t now = millis();
    if (now - s_lastSendMs >= kSendPeriodMs) {
        s_lastSendMs = now;
        setAlternatingPayload();
        sendVersionPacket();
    }
}

#if LONGDATA_USE_DMA && defined(STM32G4xx)
extern "C" void DMA1_Channel1_IRQHandler(void) {
    if (auto *p = IrDmaTxStm32<kIrDmaStreams>::instance()) {
        p->irqForStream(0);
    }
}
#endif
