#pragma once
#include "IR_config.h"
#include "RingBuffer.h"

class Print;

#define IRDEBUG

#ifdef IRDEBUG
#define wrHigh 255  // Запись HIGH инициирована                     // green
#define wrLow 255   // Запись LOW инициирована                      // blue
#define writeOp 255 // Операция записи, 1 пульс для 0 и 2 для 1     // orange
// Исправленные ошибки                                              // purle
// 1 пульс: fix
#define errOut 255
#define up 255
#define down 255
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////

#define riseTime riseSyncTime              //* bitTime */  893U        // TODO: Должно высчитываться медианой
#define riseTolerance tolerance /* 250U */ // погрешность
#define riseTimeMax (riseTime + riseTolerance)
#define riseTimeMin (riseTime - riseTolerance)
#define aroundRise(t) (riseTimeMin < t && t < riseTimeMax)
#define IR_timeout (riseTimeMax * (8 + syncBits + 1)) // us // таймаут в 8 data + 3 sync + 1
constexpr uint16_t IR_ResponseDelay = ((uint16_t)(((bitTime+riseTolerance) * (8 + syncBits + 1))*2.7735))/1000;

class IR_Encoder;
class IR_DecoderRaw : virtual public IR_FOX
{
    friend IR_Encoder;

protected:
PackInfo packInfo;
uint8_t msgTypeReceive = 0;
IR_Encoder *encoder; // Указатель на парный передатчик
bool availableRaw();

public:
    //////////////////////////////////////////////////////////////////////////
    /// @brief Конструктор
    /// @param pin Номер вывода прерывания/данных от приёмника (2 или 3 для atmega 328p)
    /// @param addr Адрес приёмника
    /// @param encPair Указатель на передатчик, работающий в паре
    IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair = nullptr);

    void isr();  // Функция прерывания
    void tick(); // Обработка приёмника, необходима для работы

    inline bool isOverflow() { return isBufferOverflow; }; // Буффер переполнился
    bool isSubOverflow();
    volatile inline bool isReciving() { return isRecive; }; // Возвращает true, если происходит приём пакета
    uint32_t pulseFilterDroppedByFilteredOverflow() const { return 0; }
    uint32_t pulseFilterDroppedByHoldOverflow() const { return pulseFilterDropHoldOverflow; }
    uint32_t pulseFilterDroppedGlitchPairs() const { return pulseFilterDropGlitchPairs; }
    void pulseFilterResetStats();

#if defined(IR_EDGE_TRACE)
    void edgeTraceClear();
    bool edgeTraceOverflow() const { return edgeTrace_overflow; }
    uint16_t edgeTracePendingCount() const;
    /** При непустом кольце: перевод строки + @IRF1v1: + hex; в tick() сброс на Serial автоматически. См. ref/IR_EDGE_TRACE_FORMAT.md */
    uint16_t edgeTraceFlushChunk(Print &out, uint16_t maxRec = 48);
#endif

    /// Кадр собран по длине из заголовка, но CRC не сошёлся — один раз можно прочитать копию сырых байтов.
    bool availableReject();
    uint8_t getRejectSize() const { return rejectPackSize; }
    const uint8_t* getRejectBuffer() const { return rejectBuffer; }

    //////////////////////////////////////////////////////////////////////////
private:
    enum class RxBriefReason : uint8_t
    {
        MuteBegin = 1,
        MuteEnd = 2,
        RawOverflow = 3,
        FilterOverflow = 4,
        HoldOverflow = 5,
        Glitch = 6,
        Timing = 7,
        Preamble = 8,
        Sync = 9,
        BufferOverflow = 10,
        Timeout = 11,
        Crc = 12,
        Ok = 13
    };

    bool isRejectAvailable = false;
    uint8_t rejectPackSize = 0;
    uint8_t rejectBuffer[dataByteSizeMax]{};

    ErrorsStruct errors;
    bool isAvailable = false;
    uint16_t packSize = 0;
    uint16_t crcValue = 0;
    volatile uint16_t isPairSending = 0; // Число активных TX, временно глушащих этот RX.
    volatile bool isRecive = false;      // Флаг приёма
    volatile bool isPreamb = false;      // флаг начальной последовости
    volatile bool isSubBufferOverflow = false;
    bool isBufferOverflow = false; // Флаг переполнения буффера данных
    bool isWrongPack = false;      // Флаг битого пакета

    uint16_t riseSyncTime = bitTime; // Подстраиваемое время бита в мкс

    volatile uint32_t lastEdgeTime = 0;   // время последнего фронта

    ////////////////////////////////////////////////////////////////////////
    volatile uint32_t currentSubBufferIndex; // Счетчик текущей позиции во вспомогательном буфере фронтов/спадов

    struct FrontStorage
    {                               // Структура для хранения времени и направления фронта/спада
        volatile uint32_t time = 0; // Время
        volatile bool dir = false;  // Направление (true = ↑; false = ↓)
        // volatile FrontStorage *next = nullptr; // Указатель на следующий связанный фронт/спад, или nullptr если конец
    };
    volatile FrontStorage *lastFront = nullptr;           // Указатель последнего фронта/спада
    volatile FrontStorage *firstUnHandledFront = nullptr; // Указатель первого необработанного фронта/спада
    // volatile FrontStorage subBuffer[subBufferSize];       // вспомогательный буфер для хранения необработанных фронтов/спадов

    RingBuffer<FrontStorage, subBufferSize> subBuffer;
    IR_Encoder *pairMuteEncoders[IR_PAIR_MUTE_MAX_ENCODERS]{};
    uint8_t pairMuteEncoderCount = 0;
    static constexpr uint8_t kPulseFilterHoldCap = 6;
    FrontStorage pulseFilterHoldEdges[kPulseFilterHoldCap]{};
    uint8_t pulseFilterHoldCount = 0;
    bool pulseFilterLastRawValid = false;
    uint32_t pulseFilterLastRawTime = 0;
    uint32_t pulseFilterDropHoldOverflow = 0;
    uint32_t pulseFilterDropGlitchPairs = 0;
    static constexpr uint8_t kPreambleLockNeed = (uint8_t)IR_PREAMBLE_LOCK_RISE_PERIODS;
    enum class PreambleState : uint8_t
    {
        Idle = 0,
        Candidate = 1,
        Locked = 2
    };
    PreambleState preambleState = PreambleState::Idle;
    uint8_t preambleGoodPeriods = 0;
    uint16_t preambleMeanPeriod = 0;
    uint32_t preambleCandidateLastEdgeTime = 0;
    uint32_t preambleCandidateFirstRiseTime = 0;
    bool preambleCandidateFirstRiseValid = false;

#if defined(IR_EDGE_TRACE)
    struct IrEdgeTraceRec
    {
        uint32_t t_us;
        uint8_t level;
        uint8_t flags;
    };
    void edgeTracePush(uint32_t t_us, uint8_t level, uint8_t flags);
    IrEdgeTraceRec edgeTrace_buf[IR_EDGE_TRACE_CAPACITY]{};
    volatile uint16_t edgeTrace_w = 0;
    volatile uint16_t edgeTrace_r = 0;
    volatile bool edgeTrace_overflow = false;
#endif

#if IR_RX_BRIEF_LOG
    volatile bool rxBriefMuteBeginPending = false;
    volatile uint32_t rxBriefMuteBeginUs = 0;
    volatile bool rxBriefMuteEndPending = false;
    volatile uint32_t rxBriefMuteEndUs = 0;
    volatile uint16_t rxBriefMuteEndCount = 0;
    volatile uint16_t rxBriefMuteBlockedEdges = 0;
    volatile uint16_t rxBriefRawOverflowDrops = 0;
    volatile uint32_t rxBriefRawOverflowLastUs = 0;
#endif

    ////////////////////////////////////////////////////////////////////////
    uint8_t dataBuffer[dataByteSizeMax]{0};                           // Буффер данных
    volatile uint32_t prevRise, prevPrevRise, prevFall, prevPrevFall; // Время предыдущих фронтов/спадов

    volatile uint32_t risePeriod;
    volatile uint32_t highTime;
    volatile uint32_t lowTime;

    uint16_t wrongCounter;

    int8_t highCount;
    int8_t lowCount;
    int8_t allCount;

    uint16_t errorCounter = 0;     // Счётчик ошибок
    int8_t preambFrontCounter = 0; // Счётчик __/``` ↑ преамбулы
    int16_t bufBitPos = 0;         // Позиция для записи бита в буффер

private:
bool isReciveRaw = false;
    void listenStart();
    void checkTimeout();                  // 
    /** В очередях/hold фильтра ещё есть фронты — не оценивать таймаут по micros()-lastEdgeTime (ложный TIMEOUT). */
    bool rxTimeoutPipelineBusy() const;
    void pulseFilterPushRaw(const FrontStorage &e);
    bool pulseFilterTryTakeConfirmed(FrontStorage &out, uint32_t logTime = 0);
    bool pulseFilterTryFlushOne(uint32_t nowUs, FrontStorage &out);
    void pulseFilterShiftLeft(uint8_t n);
    void pulseFilterReset();
    void processDecodedFront(const FrontStorage &currentFront);
    static uint32_t absDiffU32(uint32_t a, uint32_t b);
    bool registerPairMuteEncoder(IR_Encoder *enc);
    void refreshPairMuteState();
    uint32_t preambleJitterTolUs(uint32_t baselineUs) const;
    bool preambleRisePeriodCoarseOk(uint32_t periodUs) const;
    void preambleResetToIdle();
    void preambleStartCandidate(const FrontStorage &front);
    bool preambleProcessEdge(const FrontStorage &front);

    /// @brief Проверка CRC. Проверяет len байт со значением crc, пришедшим в пакете
    /// @param len Длина в байтах проверяемых данных
    /// @param crc Результат рассчёта crc (Выходной параметр)
    /// @return true если crc верно
    bool crcCheck(uint8_t len, uint16_t &crc);

    ////////////////////////////////////////////////////////////////////////
    bool isData = true;                  // Флаг относится ли бит к данным, или битам синхронизации
    uint16_t i_dataBuffer = 0;            // Счётчик буфера данных
    uint16_t nextControlBit = bitPerByte; // Метка для смены флага isData; uint16_t нужен для длинных кадров (>24 байт total)
    uint8_t i_syncBit = 0;               // Счётчик битов синхронизации
    uint8_t err_syncBit = 0;             // Счётчик ошибок синхронизации

    /// @brief Запиь бита в буффер, а так же проверка битов синхранизации и их фильтрация
    /// @param packTraceInvertFix если true — в IRDEBUG_SERIAL_PACK бит в трассе пишется как `0`/`1` (исправление по фронтам)
    void writeToBuffer(bool bit, bool packTraceInvertFix = false);
    ////////////////////////////////////////////////////////////////////////

    void firstRX(); /// @brief Установка и сброс начальных значений и флагов в готовность к приёму данных

    /// @brief Целочисленное деление с округлением вверх
    /// @param val Значение
    /// @param divider Делитель
    /// @return Результат
    uint16_t ceil_div(uint16_t val, uint16_t divider);

#if IR_RX_BRIEF_LOG
    static const __FlashStringHelper *rxBriefReasonTag(RxBriefReason reason);
    void rxBriefLog(RxBriefReason reason, uint16_t a = 0, uint16_t b = 0, uint32_t tUs = 0);
    void rxBriefNoteMuteBlockedIsr(uint32_t tUs);
    void rxBriefNoteRawOverflowIsr(uint32_t tUs);
    void rxBriefFlushDeferredIsrLogs();
#endif

#ifdef IRDEBUG
    uint32_t wrCounter;
    inline void errPulse(uint8_t pin, uint8_t count);
    inline void infoPulse(uint8_t pin, uint8_t count);
#endif

#if defined(IRDEBUG_SERIAL_PACK)
    static constexpr uint16_t kPackTraceBufCap =
        uint16_t(dataByteSizeMax) * (uint16_t(bitPerByte) + uint16_t(syncBits)) + 48u;

    void packTraceResetFrame();
    void packTracePushBit(bool bit);
    void packTracePushChar(char c);
    /** Помечает в packTraceBitBuf бит (после BRUTEFORCE_CHECK) обёрткой `0`/`1` по финальному значению в dataBuffer. */
    void packTraceWrapDataBitInBackticks(uint16_t byteIndex, uint8_t bitInByte);
    /** IR hex: все байты dataBuffer[0 .. byteCount-1] в hex. */
    void packTraceEmitHex(uint8_t byteCount) const;
    /** IR raw: биты и синхра; тройной пробел между блоками msg/from/to/data/CRC; первый байт 3+пробел+5. endWithNewline — перевод строки после сырой строки. */
    void packTraceEmitRawBitsLine(bool endWithNewline = true) const;
    void packTraceEmitErrorFlash(const __FlashStringHelper *msg);
    void packTraceEmitEndOk(uint8_t packSize);
    void packTraceEmitEndBadCrc(uint8_t packSize);
    void packTraceOnTimeoutOrAbort(bool fromListenStart);
    void packTraceForceEndSyncPhase();
    bool packTraceSoftReject() const;

    bool packTraceOpen = false;
    bool packTraceHadWrongSync = false;
    char packTraceBitBuf[kPackTraceBufCap]{};
    uint16_t packTraceLen = 0;
#endif
};
