#pragma once
#include <Arduino.h>
#include <list>
// #define IRDEBUG_INFO

/** Число потоков DMA-TX задаётся шаблоном: IrDmaTxStm32<2>, см. IrDmaTxStm32.h и irproto::kDefaultDmaTxMaxStreams. */
namespace irproto {
constexpr size_t kDefaultDmaTxMaxStreams = 4U;
/** Кольцевой буфер BSRR-слов для ISR-TX (как у DMA: два полублока). Чётное число. */
constexpr uint16_t kIsrTxBsrrWordCount = 256U;
/** Максимум RLE-сегментов для buildGateRuns при ISR-TX. */
constexpr size_t kIsrTxMaxGateRuns = 512U;
static_assert((kIsrTxBsrrWordCount & 1U) == 0U, "kIsrTxBsrrWordCount must be even");
}

// Пошаговый разбор кадра на Serial (по умолчанию выключено). Пульсы IRDEBUG на пинах не меняют.
// #define IRDEBUG_SERIAL_PACK
// Не обрывать приём сразу при накопленной sync-ошибке — «дописывать» до таймаута (только вместе с IRDEBUG_SERIAL_PACK).
// #define IRDEBUG_SERIAL_SOFT_REJECT

// Краткий лог причин, почему физический сигнал не дошёл до распознанного пакета.
// Формат и коды: ref/IR_RX_BRIEF_LOG.md
#ifndef IR_RX_BRIEF_LOG
#define IR_RX_BRIEF_LOG 0
#endif
// 1: печатать только отклонённые/ошибочные события; успехи и шумовые PREAMB скрыть.
#ifndef IR_RX_BRIEF_LOG_REJECT_ONLY
#define IR_RX_BRIEF_LOG_REJECT_ONLY 1
#endif

// Журнал фронтов ИК в ISR; сброс строк @IRF1v1: в IR_DecoderRaw::tick(). См. ref/IR_EDGE_TRACE_FORMAT.md
// Расход RAM ≈ IR_EDGE_TRACE_CAPACITY * 6 байт на декодер. Выключить — закомментировать:
// #define IR_EDGE_TRACE
#if defined(IR_EDGE_TRACE)
#ifndef IR_EDGE_TRACE_CAPACITY
#define IR_EDGE_TRACE_CAPACITY 512u
#endif
/** Запись в edgeTrace: фронт не передан в decode (isPairSending). */
#define IR_EDGE_TRACE_F_SKIP_DECODE 0x01u
#endif
// Пример в скетче: void irPackTracePrintOkCommand(const uint8_t* b, uint8_t n) { Serial.print(F("CarCmd::...")); }
#if defined(IRDEBUG_SERIAL_PACK)
/** Слабая реализация в IR_DecoderRaw.cpp; в скетче определите свою для вывода вроде CarCmd::... */
void irPackTracePrintOkCommand(const uint8_t *buf, uint8_t packSize);
#endif
/*//////////////////////////////////////////////////////////////////////////////////////

Для работы в паре положить декодер в энкодер

*/
// Адресация с 1 до 65 499
#define IR_Broadcast 65000 // 65 500 ~ 65 535 - широковещательные пакеты (всем)
/*
*Адресное пространство:
    Адрес 0 запрещен и зарезервирован под NULL, либо тесты
    IR_MSG_ACCEPT с адреса 0 воспринимается всеми устройствами
*/ 
//**** Контрольные точки ******
#define IR_MAX_ADDR_CPU 63999
#define IR_MIN_ADDR_CPU 32000

// //***** Группы машинок ********
// #define IR_MAX_CAR_GROUP 31999
// #define IR_MIN_CAR_GROUP 30000

// //********** FREE *************
// #define IR_MAX_FREE 31999
// #define IR_MIN_FREE 2000

//********* Машинки ***********
#define IR_MAX_CAR 31999
#define IR_MIN_CAR 1

//***** Пульты управления *****
#define IR_MAX_CONTROLLER 64999
#define IR_MIN_CONTROLLER 64000
/*

/```````````````````````````````````````````````` data pack `````````````````````````````````````````````\                                  
                                                                                                         
{``````````} [````````````````````````] [````````````````````] [````````````````````````] [``````````````]
{ msg type } [   addr_from uint16_t   ] [  addr_to uint16_t  ] [====== data bytes ======] [   CRC Bytes  ]
{..........} [........................] [....................] [........................] [..............]
                                                                                                          
{ aka size } [addr_from_H][addr_from_L] [addr_to_H][addr_to_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ]
|     0           1            2              3         4          5                         |       |    
\____________________________________________________________________________________________/       |    
|                                                                                                    |    
\____________________________________________________________________________________________________/    

msg type:
                                //   __________
                                //  | 01234567 |
                                //   ----------
                                //  | xxx..... | = тип сообщения (биты 7..5)
                                //  | ...xxxxx | = полная длина кадра в байтах (5 бит, 0..31, IR_MASK_MSG_INFO), не «31 бит» и не отдельный лимит «24 байта»
                                //  Полезная нагрузка в data pack: до bytePerPack байт (см. #define bytePerPack).
                                //   ---------- */
#define IR_MSG_BACK 0U          //  | 000...... | = Задний сигнал машинки
#define IR_MSG_ACCEPT 1U        //  | 001..... | = подтверждение
#define IR_MSG_REQUEST 2U       //  | 010..... | = запрос
// #define IR_MSG_ 3U           //  | 011..... | = ??
#define IR_MSG_BACK_TO 4U       //  | 100..... | = Задний сигнал машинки c адресацией
// #define IR_MSG_ 5U           //  | 101..... | = ??
#define IR_MSG_DATA_NOACCEPT 6U //  | 110..... | = данные, не требующие подтверждения
#define IR_MSG_DATA_ACCEPT 7U   //  | 111..... | = данные требующие подтверждения
;                               /*                                     //   ----------
                              
                               /``````````````````````````````` подтверждение `````````````````````````````\      /``````````````````````````````````````` запрос ``````````````````````````````````\
                                                                                                                                                     
                               {``````````} [````````````````````````] [``````````````````] [``````````````]      {``````````} [````````````````````````] [````````````````````````] [``````````````]
                               { msg type } [   addr_from uint16_t   ] [=== customByte ===] [   CRC Bytes  ]      { msg type } [   addr_from uint16_t   ] [    addr_to uint16_t    ] [   CRC Bytes  ]
                               {..........} [........................] [..................] [..............]      {..........} [........................] [........................] [..............]
                                                                                                                                                                                           
                               { 001..... } [addr_from_H][addr_from_L] [=== customByte ===] [ crc1 ][ crc2 ]      { 010..... } [addr_from_H][addr_from_L] [addr_from_H][addr_from_L] [ crc1 ][ crc2 ]
                               |     0            1           2                  3              4       5          |     0            1           2              3           4           5       6    
                               \________________________________________________________________/       |          \_____________________________________________________________________/       |    
                               |                                                                        |          |                                                                             |    
                               \________________________________________________________________________/          \_____________________________________________________________________________/    
                              
                               customByte - контрольная сумма принятых данных по poly1
                              
                              
                              
                               /`````````````````````` Задний сигнал машинки без адресации ``````````````````````\        
                               // Первый байт: (IR_MSG_BACK<<5) | (packSize & IR_MASK_MSG_INFO) — как у data pack (тип + длина 0..31).
                                                                                                                          
                               {``````````} [````````````````````````] [````````````````````````] [``````````````]        
                               { msg type } [   addr_from uint16_t   ] [====== data bytes ======] [   CRC Bytes  ]        
                               {..........} [........................] [........................] [..............]        
                                                                                                                          
                               { xxx..|..xxxxx } [addr_from_H][addr_from_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ]        
                               |     0           1            2            3                         |       |            
                               \_____________________________________________________________________/       |            
                               |                                                                             |            
                               \_____________________________________________________________________________/            
                              
                              
                              
                               /```````````````````````````````````` Задний сигнал машинки с адресацией ````````````````````````````````````\ 
                               // Первый байт: (IR_MSG_BACK_TO<<5) | (packSize & IR_MASK_MSG_INFO) — IR_MSG_BACK_TO в битах 7..5, длина 0..31.
                                                                                                                         
                               {``````````} [````````````````````````] [````````````````````````] [````````````````````````] [``````````````] 
                               { msg type } [   addr_from uint16_t   ] [    addr_to uint16_t    ] [====== data bytes ======] [   CRC Bytes  ] 
                               {..........} [........................] [........................] [........................] [..............] 
                                                                                                                                               
                               { xxx..|..xxxxx } [addr_from_H][addr_from_L] [addr_to_H][addr_to_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ] 
                               |     0           1            2              3           4            5                         |       |     
                               \________________________________________________________________________________________________/       |     
                               |                                                                                                        |     
                               \________________________________________________________________________________________________________/     
                              
                               */

#define IR_MASK_MSG_TYPE 0b00000111
#define IR_MASK_MSG_INFO 0b00011111

/*
/////////////////////////////////////////////////////////////////////////////////////*/
typedef uint16_t crc_t;

// #define BRUTEFORCE_CHECK                            // Перепроверяет пакет на 1 битные ошибки //TODO: зависает
#define bytePerPack (31) // колличество байтов в пакете
#ifndef freeFrec
#define freeFrec false
#endif

#ifndef subBufferSize
#define subBufferSize 250 // Буфер для складирования фронтов, пока их не обработают (передатчик)
#endif

/** Максимальное число передатчиков, способных временно заглушить один декодер. */
#ifndef IR_PAIR_MUTE_MAX_ENCODERS
#define IR_PAIR_MUTE_MAX_ENCODERS 8U
#endif

/** Минимальная длительность удержания уровня (мкс): короче — импульс/пара фронтов выкидывается до tick()
 *  (иголки на плато, дребезг). 0 — фильтр выключен, фронты идут в декодер как с ISR. */
#ifndef IR_INPUT_MIN_PULSE_US
#define IR_INPUT_MIN_PULSE_US 0
#endif
/** Сколько подтверждённых фронтов держать перед выпуском в декодер (потоковая задержка). */
#ifndef IR_INPUT_FILTER_HOLDBACK_EDGES
#define IR_INPUT_FILTER_HOLDBACK_EDGES 3U
#endif
/** Если новых фронтов нет, через minPulse*mult держатель принудительно сбрасывается в декодер. */
#ifndef IR_INPUT_FILTER_TIMEOUT_MULT
#define IR_INPUT_FILTER_TIMEOUT_MULT 5U
#endif

/** Синхронно с IrFoxProtocolConstants.h / IrFoxDecoder (плагин Saleae). */
#ifndef IR_SHORT_LOW_GLITCH_REJECT
#define IR_SHORT_LOW_GLITCH_REJECT 1
#endif
#ifndef IR_GLITCH_REJECT_PHASE_NUDGE
#define IR_GLITCH_REJECT_PHASE_NUDGE 1
#endif
#ifndef IR_MICRO_GAP_RISE_REJECT
#define IR_MICRO_GAP_RISE_REJECT 1
#endif
/** Лок преамбулы: сколько одинаковых подряд периодов подъёма нужно для старта кадра. */
#ifndef IR_PREAMBLE_LOCK_RISE_PERIODS
#define IR_PREAMBLE_LOCK_RISE_PERIODS 2U
#endif
/** Допуск одинаковости периода преамбулы (проценты) + минимальная абсолютная полка в мкс. */
#ifndef IR_PREAMBLE_JITTER_PCT
#define IR_PREAMBLE_JITTER_PCT 18U
#endif
#ifndef IR_PREAMBLE_JITTER_US_MIN
#define IR_PREAMBLE_JITTER_US_MIN 80U
#endif
/** Грубое окно валидности периода преамбулы RISE->RISE (в процентах от bitTime).
 *  Для текущего протокола преамбула заметно длиннее обычного битового периода. */
#ifndef IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT
#define IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT 220U
#endif
#ifndef IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT
#define IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT 340U
#endif
/** Таймаут окна кандидата преамбулы: IR_timeout * mult. */
#ifndef IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT
#define IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT 3U
#endif

#define preambPulse 3

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

#define bitPerByte 8U // Колличество бит в байте
#define addrBytes 2
#define msgBytes 1
#define crcBytes 2
#define poly1 0x31
#define poly2 0x8C
#define syncBits 3U // количество битов синхронизации

#define dataByteSizeMax (msgBytes + addrBytes + addrBytes + bytePerPack + crcBytes)

#define preambFronts (preambPulse * 2)                              // количество фронтов преамбулы (Приём)
#define preambToggle ((bitPauseTakts * 2 + bitActiveTakts) * 2 - 1) // колличество переключений преамбулы (Передача)

#define carrierFrec 38000U                     // частота несущей (Приём/Передача)
#define carrierPeriod (1000000U / carrierFrec) // период несущей в us (Приём)

// В процессе работы значения будут отклонятся в соответствии с предыдущим битом
#define bitActiveTakts 25U // длительность высокого уровня в тактах
#define bitPauseTakts 12U  // длительность низкого уровня в тактах

#define bitTakts (bitActiveTakts + bitPauseTakts) // Общая длительность бита в тактах
#define bitTime (bitTakts * carrierPeriod)        // Общая длительность бита
#define tolerance 300U

constexpr uint16_t test_all_Time = bitTime;
constexpr uint16_t test_all_Takts = bitTakts * 2;
constexpr uint16_t test_hi = ((bitPauseTakts) * 2 - 0) + ((bitActiveTakts) * 2 - 0);
constexpr uint16_t test_low = ((bitPauseTakts / 2 + bitActiveTakts) * 2 - 0) + ((bitPauseTakts)-0);

class IR_FOX
{
public:
    struct PackOffsets
    {
        uint8_t msgOffset;
        uint8_t addrFromOffset;
        uint8_t addrToOffset;
        uint8_t dataOffset;
        uint8_t crcOffset;
    };

    struct ErrorsStruct
    {
        uint8_t lowSignal = 0;
        uint8_t highSignal = 0;
        uint8_t other = 0;

        void reset()
        {
            lowSignal = 0;
            highSignal = 0;
            other = 0;
        }
        uint16_t all() { return lowSignal + highSignal + other; }
    };

    struct PackInfo
    {
        uint8_t *buffer = nullptr;
        uint8_t packSize = 0;
        uint16_t crc = 0;
        ErrorsStruct err;
        uint16_t rTime = 0;
    };

    inline uint16_t getId() const { return id; }
    inline void setId(uint16_t id) { this->id = id; }
    static void checkAddressRuleApply(uint16_t address, uint16_t id, bool &flag);
    void setPin(uint8_t pin);
    inline uint8_t getPin() { return pin; };
    inline GPIO_TypeDef *getPort() const { return port; }
    inline uint16_t getPinMask() const { return mask; }

protected:
    uint16_t id;
    uint8_t pin;
    GPIO_TypeDef *port;
    uint16_t mask;
    ErrorsStruct errors;
    uint8_t crc8(uint8_t *data, uint8_t start, uint8_t end, uint8_t poly);
};
