/*
 * IRReceive.hpp
 * This file is exclusively included by IRremote.h to enable easy configuration of library switches
 *
 *  Contains all protocol functions used by receiver and sender.
 *
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 ************************************************************************************
 * MIT License
 *
 * Copyright (c) 2009-2023 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************************
 */
#ifndef _IR_PROTOCOL_HPP
#define _IR_PROTOCOL_HPP

#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif

/** \addtogroup Receiving Receiving IR data for multiple protocols
 * @{
 */

const char string_Unknown[] PROGMEM = "UNKNOWN";
const char string_PulseWidth[] PROGMEM = "PulseWidth";
const char string_PulseDistance[] PROGMEM = "PulseDistance";
const char string_Apple[] PROGMEM = "Apple";
const char string_Denon[] PROGMEM = "Denon";
const char string_JVC[] PROGMEM = "JVC";
const char string_LG[] PROGMEM = "LG";
const char string_LG2[] PROGMEM = "LG2";
const char string_NEC[] PROGMEM = "NEC";
const char string_NEC2[] PROGMEM = "NEC2";
const char string_Onkyo[] PROGMEM = "Onkyo";
const char string_Panasonic[] PROGMEM = "Panasonic";
const char string_Kaseikyo[] PROGMEM = "Kaseikyo";
const char string_Kaseikyo_Denon[] PROGMEM = "Kaseikyo_Denon";
const char string_Kaseikyo_Sharp[] PROGMEM = "Kaseikyo_Sharp";
const char string_Kaseikyo_JVC[] PROGMEM = "Kaseikyo_JVC";
const char string_Kaseikyo_Mitsubishi[] PROGMEM = "Kaseikyo_Mitsubishi";
const char string_RC5[] PROGMEM = "RC5";
const char string_RC6[] PROGMEM = "RC6";
const char string_RC6A[] PROGMEM = "RC6A";
const char string_Samsung[] PROGMEM = "Samsung";
const char string_SamsungLG[] PROGMEM = "SamsungLG";
const char string_Samsung48[] PROGMEM = "Samsung48";
const char string_Sharp[] PROGMEM = "Sharp";
const char string_Sony[] PROGMEM = "Sony";
const char string_BangOlufsen[] PROGMEM = "Bang&Olufsen";
const char string_BoseWave[] PROGMEM = "BoseWave";
const char string_Lego[] PROGMEM = "Lego";
const char string_MagiQuest[] PROGMEM = "MagiQuest";
const char string_Whynter[] PROGMEM = "Whynter";
const char string_FAST[] PROGMEM = "FAST";
const char string_Other[] PROGMEM = "OTHER";

/*
 * !!Must be the same order as in decode_type_t in IRProtocol.h!!!
 */
const char *const ProtocolNames[]
PROGMEM = { string_Unknown, string_PulseWidth, string_PulseDistance, string_Apple, string_Denon, string_JVC, string_LG, string_LG2,
        string_NEC, string_NEC2, string_Onkyo, string_Panasonic, string_Kaseikyo, string_Kaseikyo_Denon, string_Kaseikyo_Sharp,
        string_Kaseikyo_JVC, string_Kaseikyo_Mitsubishi, string_RC5, string_RC6, string_RC6A, string_Samsung, string_SamsungLG,
        string_Samsung48, string_Sharp, string_Sony
#if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
        , string_BangOlufsen, string_BoseWave, string_Lego, string_MagiQuest, string_Whynter, string_FAST, string_Other
#endif
        };

#if defined(__AVR__)
const __FlashStringHelper* getProtocolString(decode_type_t aProtocol) {
    const char *tProtocolStringPtr = (char*) pgm_read_word(&ProtocolNames[aProtocol]);
    return ((__FlashStringHelper*) (tProtocolStringPtr));
}
#else
const char* getProtocolString(decode_type_t aProtocol) {
    return ProtocolNames[aProtocol];
}
#endif

#if (__INT_WIDTH__ >= 32)
#  if __has_include(<type_traits>)
/*
 * This code to handle the missing print(unsigned long long...) function of seeduino core was contributed by sklott
 * https://stackoverflow.com/questions/74622227/avoid-calling-of-function-size-t-printprintunsigned-long-long-n-int-base-if
 */
#include <type_traits>

// If you have C++17 you can just use std::void_t, or use this for all versions
#if __cpp_lib_void_t >= 201411L
template<typename T>
using void_t = std::void_t<T>;
#else
template<typename ... Ts> struct make_void {
    typedef void type;
};
template<typename ... Ts> using void_t = typename make_void<Ts...>::type;
#endif

// Detecting if we have print(unsigned long long value, int base) / print(0ull, 0) overload
template<typename T, typename = void>
struct has_ull_print: std::false_type {
};
template<typename T>
struct has_ull_print<T, void_t<decltype(std::declval<T>().print(0ull, 0))>> : std::true_type {
};

// Must be namespace, to avoid public and static declarations for class
namespace PrintULL {
template<typename PrintImplType, typename std::enable_if<!has_ull_print<PrintImplType>::value, bool>::type = true>
size_t print(PrintImplType *p, unsigned long long value, int base) {
    size_t tLength = p->print(static_cast<uint32_t>(value >> 32), base);
    tLength += p->print(static_cast<uint32_t>(value), base);
    return tLength;
}

template<typename PrintImplType, typename std::enable_if<has_ull_print<PrintImplType>::value, bool>::type = true>
size_t print(PrintImplType *p, unsigned long long value, int base) {
    return p->print(value, base);
}
}
;
#  else
namespace PrintULL {
    size_t print(Print *aSerial, unsigned long long n, int base) {
        return aSerial->print(n, base);
    }
};
#  endif
#endif

/** @}
 * \addtogroup Utils Utility functions
 * @{
 */
/**********************************************************************************************************************
 * Function to bit reverse OLD MSB values of e.g. NEC.
 **********************************************************************************************************************/
uint8_t bitreverseOneByte(uint8_t aValue) {
//    uint8_t tReversedValue;
//    return __builtin_avr_insert_bits(0x01234567, aValue, tReversedValue);
// 76543210
    aValue = (aValue >> 4) | (aValue << 4); // Swap in groups of 4
// 32107654
    aValue = ((aValue & 0xcc) >> 2) | ((aValue & 0x33) << 2); // Swap in groups of 2
// 10325476
    aValue = ((aValue & 0xaa) >> 1) | ((aValue & 0x55) << 1); // Swap bit pairs
// 01234567
    return aValue;
}

uint32_t bitreverse32Bit(uint32_t aInput) {
//    __builtin_avr_insert_bits();
    LongUnion tValue;
    tValue.UByte.HighByte = bitreverseOneByte(aInput);
    tValue.UByte.MidHighByte = bitreverseOneByte(aInput >> 8);
    tValue.UByte.MidLowByte = bitreverseOneByte(aInput >> 16);
    tValue.UByte.LowByte = bitreverseOneByte(aInput >> 24);
    return tValue.ULong;
}

/** @}*/

#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_PROTOCOL_HPP
