#include "IR_Decoder.h"

#if defined(ARDUINO_ARCH_STM32) && !defined(HAL_EXTI_MODULE_DISABLED)
#include "Arduino.h"
/* NVIC_SetPriority — CMSIS, как в IR_Encoder::begin и Car.ino (без HAL-заголовка yyxx). */

/** NVIC для линии EXTI пина (как в Arduino STM32 SrcWrapper interrupt.cpp). */
static IRQn_Type ir_decoder_exti_irqn_for_pin(uint8_t arduino_pin)
{
#if defined(STM32C0xx) || defined(STM32F0xx) || defined(STM32G0xx) || defined(STM32L0xx)
    (void)arduino_pin;
    return (IRQn_Type)(-1);
#else
    const PinName p = digitalPinToPinName(arduino_pin);
    if (p == NC) {
        return (IRQn_Type)(-1);
    }
    const uint16_t pinmask = STM_GPIO_PIN(p);
    uint8_t id = 0U;
    uint16_t pm = pinmask;
    while (pm != 0x0001U) {
        pm = (uint16_t)(pm >> 1U);
        id++;
    }
#if defined(STM32H5xx) || defined(STM32MP1xx) || defined(STM32L5xx) || defined(STM32U5xx) || defined(STM32WBAxx)
    static const IRQn_Type exti_irqnb[16] = {
        EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn, EXTI5_IRQn, EXTI6_IRQn,
        EXTI7_IRQn, EXTI8_IRQn, EXTI9_IRQn, EXTI10_IRQn, EXTI11_IRQn,
        EXTI12_IRQn, EXTI13_IRQn, EXTI14_IRQn, EXTI15_IRQn};
#else
    static const IRQn_Type exti_irqnb[16] = {
        EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn,
        EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn,
        EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn,
        EXTI15_10_IRQn, EXTI15_10_IRQn};
#endif
    if (id < 16U) {
        return exti_irqnb[id];
    }
    return (IRQn_Type)(-1);
#endif
}

static void ir_decoder_apply_rx_exti_nvic(uint8_t arduino_pin, uint32_t preempt)
{
    const IRQn_Type irqn = ir_decoder_exti_irqn_for_pin(arduino_pin);
    if ((int)irqn < 0) {
        return;
    }
#if !defined(STM32C0xx) && !defined(STM32F0xx) && !defined(STM32G0xx) && !defined(STM32L0xx)
    NVIC_SetPriority(irqn, preempt);
#endif
}

void IR_Decoder::setReceiveExtiPreemptPriority(uint32_t preempt)
{
    rxExtiPreemptConfigured_ = true;
    rxExtiPreemptValue_ = preempt;
    if (extiEnabled_) {
        ir_decoder_apply_rx_exti_nvic(pin, preempt);
    }
}
#endif /* ARDUINO_ARCH_STM32 && !HAL_EXTI_MODULE_DISABLED */

std::list<IR_Decoder *> &IR_Decoder::get_dec_list() // определение функции
{
    static std::list<IR_Decoder *> dec_list; // статическая локальная переменная
    return dec_list;                         // возвращается ссылка на переменную
}

// IR_Decoder::IR_Decoder() {};
IR_Decoder::IR_Decoder(const uint8_t pin, uint16_t addr, IR_Encoder *encPair, bool enableOnConstruct)
    : IR_DecoderRaw(pin, addr, encPair)
{
    get_dec_list().push_back(this);
    if (enableOnConstruct) {
        enable();
    }
};

void IR_Decoder::enable()
{
    auto &dec_list = get_dec_list();
    if (std::find(dec_list.begin(), dec_list.end(), this) == dec_list.end())
    {
        dec_list.push_back(this);
    }
    pinMode(pin, INPUT_PULLUP);
    attachInterrupt(pin, (*this)(), CHANGE);
    extiEnabled_ = true;
#if defined(ARDUINO_ARCH_STM32) && !defined(HAL_EXTI_MODULE_DISABLED)
    if (rxExtiPreemptConfigured_) {
        ir_decoder_apply_rx_exti_nvic(pin, rxExtiPreemptValue_);
    }
#endif
}

void IR_Decoder::disable()
{
    extiEnabled_ = false;
    detachInterrupt(pin);
    pinMode(pin, INPUT);
    auto &dec_list = get_dec_list();
    auto it = std::find(dec_list.begin(), dec_list.end(), this);
    if (it != dec_list.end())
    {
        dec_list.erase(it);
    }
}


std::function<void()> IR_Decoder::operator()()
{
    return std::bind(&IR_Decoder::isr, this);
}

IR_Decoder::~IR_Decoder()
{
    IR_Decoder::get_dec_list().remove(this);
}

void IR_Decoder::tick()
{
    for (const auto &element : IR_Decoder::get_dec_list())
    {
        element->_tick();
    }
}

void IR_Decoder::_tick()
{
    IR_DecoderRaw::tick();

    if (availableRaw())
    {
        isWaitingAcceptSend = false;
        switch (packInfo.buffer[0] >> 5 & IR_MASK_MSG_TYPE)
        {
        case IR_MSG_DATA_ACCEPT:
        case IR_MSG_DATA_NOACCEPT:
            gotData.set(&packInfo, id);
            break;
        case IR_MSG_BACK:
        case IR_MSG_BACK_TO:
            gotBackData.set(&packInfo, id);
            break;
        case IR_MSG_REQUEST:
            gotRequest.set(&packInfo, id);
            break;
        case IR_MSG_ACCEPT:
            gotAccept.set(&packInfo, id);
            break;

        default:
            break;
        }
        if (gotData.isAvailable && (gotData.getMsgType() == IR_MSG_DATA_ACCEPT))
        {
            acceptSendTimer = millis();
            addrAcceptSendTo = gotData.getAddrFrom();
            acceptCustomByte = crc8(gotData.getDataPrt(), 0, gotData.getDataSize(), poly1);
            if (addrAcceptSendTo && addrAcceptSendTo < IR_Broadcast)
                isWaitingAcceptSend = true;
        }
        gotRaw.set(&packInfo, id);
    }
    if (isWaitingAcceptSend && millis() - acceptSendTimer > acceptDelay)
    {
        encoder->sendAccept(addrAcceptSendTo, acceptCustomByte);
        isWaitingAcceptSend = false;
    }
}

bool IR_Decoder::isReceive(uint8_t type) {
    return (msgTypeReceive & 0b11111000) && ((msgTypeReceive & IR_MASK_MSG_TYPE) == type);
} 
