/*
 * IRReceive.hpp
 * This file is exclusively included by IRremote.h to enable easy configuration of library switches
 *
 *  Contains all IRrecv class functions as well as other receiver related functions.
 *
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 ************************************************************************************
 * MIT License
 *
 * Copyright (c) 2009-2026 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_RECEIVE_HPP
#define _IR_RECEIVE_HPP

// This block must be located after the includes of other *.hpp files
//#define LOCAL_DEBUG // This enables debug output only for this file - only for development
//#define LOCAL_TRACE // This enables trace output only for this file - only for development
#include "LocalDebugLevelStart.h"
/*
 * Low level hardware timing measurement
 */
//#define _IR_MEASURE_TIMING // for ISR
//#define _IR_TIMING_TEST_PIN 7 // "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" is executed at start()
//
#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE)
#define LED_RECEIVE_FEEDBACK_CODE // Resolve the double negative
#endif

unsigned long sMicrosAtLastStopTimer = 0; // Used to adjust TickCounterForISR with uncounted ticks between stopTimer() and restartTimer()

/** \addtogroup Receiving Receiving IR data for multiple protocols
 * @{
 */
/**
 * The receiver instance
 */
IRrecv IrReceiver;

/**
 * Instantiate the IRrecv class. Multiple instantiation is supported by activating SUPPORT_MULTIPLE_RECEIVER_INSTANCES and providing
 * the simple function UserIRReceiveTimerInterruptHandler(). This function is required, because we have only one timer resource.
 * See the MultipleReceivers example.
 * @param IRReceivePin Arduino pin to use. No sanity check is made.
 */
IRrecv::IRrecv() {
    setReceivePin(0);
}

IRrecv::IRrecv(uint_fast8_t aReceivePin) {
    setReceivePin(aReceivePin);
}

/**
 * Instantiate the IRrecv class.
 * @param aReceivePin Arduino pin to use, where a demodulating IR receiver is connected.
 * @param aFeedbackLEDPin if 0xFF, then take board specific LED_BUILTIN pin if it is defined as macro
 */
IRrecv::IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin) {
    setReceivePin(aReceivePin);
#if defined(LED_RECEIVE_FEEDBACK_CODE)
    setLEDFeedbackPin(aFeedbackLEDPin);
#else
    (void) aFeedbackLEDPin;
#endif
}

/**********************************************************************************************************************
 * Interrupt Service Routine - Called every 50 us
 *
 * Duration in ticks of 50 us of alternating SPACE, MARK are recorded in irparams.rawbuf array.
 * 'rawlen' counts the number of entries recorded so far.
 * First entry is the SPACE between transmissions.
 *
 * As soon as one SPACE entry gets longer than RECORD_GAP_TICKS, state switches to STOP (frame received). Timing of SPACE continues.
 * A call of resume() switches from STOP to IDLE.
 * As soon as first MARK arrives in IDLE, gap width is recorded and new logging starts.
 *
 * With digitalRead and Feedback LED
 * 15 pushs, 1 in, 1 eor before start of code = 2 us @16MHz + * 7.2 us computation time (6us idle time) + * pop + reti = 2.25 us @16MHz => 10.3 to 11.5 us @16MHz
 * With portInputRegister and mask and Feedback LED code commented
 * 9 pushs, 1 in, 1 eor before start of code = 1.25 us @16MHz + * 2.25 us computation time + * pop + reti = 1.5 us @16MHz => 5 us @16MHz
 * => Minimal CPU frequency is 4 MHz
 *
 **********************************************************************************************************************/
#if defined(ESP8266) || defined(ESP32)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvolatile"
IRAM_ATTR
#endif

void IRrecv::ReceiveInterruptHandler() {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
    digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
// 7 - 8.5 us for ISR body (without pushes and pops) for ATmega328 @16MHz

#if defined(TIMER_REQUIRES_RESET_INTR_PENDING)
    timerResetInterruptPending(); // reset TickCounterForISR interrupt flag if required (currently only for Teensy and ATmega4809)
#endif

// Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on]
#if defined(__AVR__)
    uint8_t tIRInputLevel = *irparams.IRReceivePinPortInputRegister & irparams.IRReceivePinMask;
#else
    uint_fast8_t tIRInputLevel = (uint_fast8_t) digitalReadFast(irparams.IRReceivePin);
#endif

    uint_fast16_t tTickCounterForISR = irparams.TickCounterForISR;
    /*
     * Increase TickCounter and clip it at maximum 0xFFFF / 3.2 seconds at 50 us ticks
     */
    if (irparams.TickCounterForISR < UINT16_MAX) {
        tTickCounterForISR++;  // One more 50uS tick
        irparams.TickCounterForISR = tTickCounterForISR;
    }

    /*
     * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32
     * So we change the code to if / else if
     */
//    switch (irparams.StateForISR) {
//
    uint_fast8_t tStateForISR = irparams.StateForISR;
    if (tStateForISR == IR_REC_STATE_IDLE) {
        /*
         * Here we are just resumed and wait for start bit. But in real time we may be in the middle of an ongoing transmission!
         */
        if (tIRInputLevel == INPUT_MARK) {
            // Check if we are not in the middle of an ongoing transmission by checking the minimum length of leading space
            if (tTickCounterForISR > RECORD_GAP_TICKS) {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//                digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
                /*
                 * Big gap between two transmissions just ended; Record gap duration + start recording transmission
                 * Initialize all state machine variables
                 */
                irparams.OverflowFlag = false;
                // irparams.rawbuf[0] = irparams.TickCounterForISR;
                // Usage of initialGapTicks enables usage of 8 bit buffer instead of 16 bit since 4.4,
                // because the big gap value is not stored in this buffer any more
                irparams.initialGapTicks = tTickCounterForISR;
                irparams.rawlen = 1;
                irparams.StateForISR = IR_REC_STATE_MARK;
            } // otherwise stay in idle state
            irparams.TickCounterForISR = 0; // reset counter in both cases
        }

    } else if (tStateForISR == IR_REC_STATE_MARK) {
        // Timing mark here, rawlen is even
        if (tIRInputLevel != INPUT_MARK) {
            /*
             * Mark ended here. Record mark time in rawbuf array
             */
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//            digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
#if !defined(USE_16_BIT_TIMING_BUFFER)
            // Clip timings > 12750 us (255 * 50) to 12750
            if (tTickCounterForISR > UINT8_MAX) {
                tTickCounterForISR = UINT8_MAX;
            }
#endif
            irparams.rawbuf[irparams.rawlen++] = tTickCounterForISR; // record mark
            irparams.StateForISR = IR_REC_STATE_SPACE;
            irparams.TickCounterForISR = 0; // This resets the tick counter also at end of frame :-)
        }

    } else if (tStateForISR == IR_REC_STATE_SPACE) {
        /*
         * Timing space here, rawlen is odd
         * Check for timeout or overflow
         */
        if (tTickCounterForISR > RECORD_GAP_TICKS || irparams.rawlen >= RAW_BUFFER_LENGTH - 1) {
            if (irparams.rawlen >= RAW_BUFFER_LENGTH) {
                // Flag up a read OverflowFlag; Stop the state machine
                irparams.OverflowFlag = true;
            }
            /*
             * Overflow or maximum space duration reached here.
             * Current code is ready for processing!
             * We received a long space, which indicates gap between codes.
             * Switch to IR_REC_STATE_STOP
             * Don't reset TickCounterForISR; keep counting width of next leading space
             */
            /*
             * These 2 variables allow to call resume() directly after decode.
             * After resume(), irparams.initialGapTicks and irparams.rawlen are
             * the first variables, which are overwritten by the next received frame.
             * since 4.3.0.
             * For backward compatibility, there are the same 2 statements in decode() if IrReceiver is not used.
             */
            IrReceiver.decodedIRData.initialGapTicks = irparams.initialGapTicks;
            IrReceiver.decodedIRData.rawlen = irparams.rawlen;

            irparams.StateForISR = IR_REC_STATE_STOP; // This signals the decode(), that a complete frame was received
#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK)
            /*
             * Call callback if registered (not nullptr)
             */
            if (irparams.ReceiveCompleteCallbackFunction != nullptr) {
#if defined(SUPPORT_MULTIPLE_RECEIVER_INSTANCES) && !defined(ARDUINO_ARCH_MBED) && !defined(ESP32) // no Serial etc. possible in callback for RTOS based cores like ESP, even when interrupts are enabled
                interrupts(); // To enable the receiving for the other instances while in callback
#endif
                irparams.ReceiveCompleteCallbackFunction();
            }
#endif
        } else if (tIRInputLevel == INPUT_MARK) {
            /*
             * Space ended here.
             */

#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//                digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
#if !defined(USE_16_BIT_TIMING_BUFFER)
            // Clip timings > 12750 us (255 * 50) to 12750
            if (tTickCounterForISR > UINT8_MAX) {
                tTickCounterForISR = UINT8_MAX;
            }
#endif
            irparams.rawbuf[irparams.rawlen++] = tTickCounterForISR; // record space
            irparams.StateForISR = IR_REC_STATE_MARK;
            irparams.TickCounterForISR = 0;
        }
    } else if (tStateForISR == IR_REC_STATE_STOP) {
        /*
         * Complete command received
         * stay here until resume() is called, which switches state to IR_REC_STATE_IDLE
         */
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//        digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
        if (tIRInputLevel == INPUT_MARK) {
            // Reset gap TickCounterForISR, to prepare for detection if we are in the middle of a transmission after call of resume()
            irparams.TickCounterForISR = 0;
        }
    }

#if defined(LED_RECEIVE_FEEDBACK_CODE)
    if (FeedbackLEDControl.LedFeedbackEnabled) {
        setFeedbackLED(tIRInputLevel == INPUT_MARK);
    }
#endif

#ifdef _IR_MEASURE_TIMING
    digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
#endif

}
/*
 * The handler which directly calls the interrupt handler function of the IRrecv object.
 * This must be a separate function, because we need this static function in IRTimer.hpp.
 * Doing it this way, we are able to modify the body of this function to support more than 2 IRrecv instances for receiving
 */
#if defined(ESP8266) || defined(ESP32)
IRAM_ATTR
#endif
void IRReceiveTimerInterruptHandler() {
    IrReceiver.ReceiveInterruptHandler();
#if defined(SUPPORT_MULTIPLE_RECEIVER_INSTANCES)
    // Quick and dirty solution by used defined extension
    UserIRReceiveTimerInterruptHandler();
#endif
}

/**********************************************************************************************************************
 * Interrupt Service Routine - Called every 50 us
 * This in turn calls calls the static interrupt handler function, which in turn calls the interrupt handler function of the IRrecv object
 */
#if defined(TIMER_INTR_NAME) || defined(ISR)
#  if defined(TIMER_INTR_NAME)
ISR (TIMER_INTR_NAME) // for ISR definitions
#  elif defined(ISR)
ISR()
// for functions definitions which are called by separate (board specific) ISR
#  endif
{
    IRReceiveTimerInterruptHandler();
}
#endif

/**********************************************************************************************************************
 * Stream like API
 **********************************************************************************************************************/

/**
 * Initializes the receive and feedback pin
 * @param aReceivePin The Arduino pin number, where a demodulating IR receiver is connected.
 * @param aEnableLEDFeedback if true / ENABLE_LED_FEEDBACK, then let the feedback led blink on receiving IR signal
 * @param aFeedbackLEDPin if 0xFF, then take board specific LED_BUILTIN pin if it is defined as macro
 */
void IRrecv::begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {

    setReceivePin(aReceivePin);
#if defined(LED_RECEIVE_FEEDBACK_CODE)
    setLEDFeedback(aEnableLEDFeedback);
    setLEDFeedbackPin(aFeedbackLEDPin);
#else
    (void) aEnableLEDFeedback;
    (void) aFeedbackLEDPin;
#endif

#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
    start();
}

/**
 * Sets / changes the receiver pin number
 */
void IRrecv::setReceivePin(uint_fast8_t aReceivePinNumber) {
    irparams.IRReceivePin = aReceivePinNumber;
#if defined(__AVR__)
#  if defined(__digitalPinToBit)
    if (__builtin_constant_p(aReceivePinNumber)) {
        irparams.IRReceivePinMask = 1UL << (__digitalPinToBit(aReceivePinNumber));
    } else {
        irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber); // requires 10 bytes PGM, even if not referenced (?because it is assembler code?)
    }
#  else
    irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber); // requires 10 bytes PGM, even if not referenced (?because it is assembler code?)
#  endif
#  if defined(__digitalPinToPINReg)
    /*
     * This code is 54 bytes smaller, if aReceivePinNumber is a constant :-), but 38 byte longer if it is not constant (,which is not likely).
     */
    if (__builtin_constant_p(aReceivePinNumber)) {
        irparams.IRReceivePinPortInputRegister = __digitalPinToPINReg(aReceivePinNumber);
    } else {
        irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber)); // requires 44 bytes PGM, even if not referenced
    }
#  else
    irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber)); // requires 44 bytes PGM, even if not referenced
#  endif
#endif
    // Seems to be at least required by ESP32
    // Set pin mode once. pinModeFast makes no difference if used, but saves 224 if not referenced :-(
    if (__builtin_constant_p(aReceivePinNumber)) {
        pinModeFast(aReceivePinNumber, INPUT);
    } else {
        pinModeFast(aReceivePinNumber, INPUT);
    }
}

#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK)
/**
 * Sets the function to call if a complete protocol frame has arrived
 */
void IRrecv::registerReceiveCompleteCallback(void (*aReceiveCompleteCallbackFunction)(void)) {
    irparams.ReceiveCompleteCallbackFunction = aReceiveCompleteCallbackFunction;
}
#endif

/**
 * Start the receiving process.
 * This configures the timer and the state machine for IR reception
 * and enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us.
 */
void IRrecv::start() {

    // Setup for cyclic 50 us interrupt
    timerConfigForReceive(); // no interrupts enabled here!

    // Initialize state machine state
    resume();

    // Timer interrupt is enabled after state machine reset
    timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us.
#ifdef _IR_MEASURE_TIMING
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
}

/*
 * Restarts timer interrupts, adjusts TickCounterForISR for correct gap value after stopTimer(). Does not call resume()!
 */
void IRrecv::restartTimer() {
    // Setup for cyclic 50 us interrupt
    timerConfigForReceive(); // no interrupts enabled here!
    // Timer interrupt is enabled after state machine reset
    if (sMicrosAtLastStopTimer != 0) {
        irparams.TickCounterForISR += (micros() - sMicrosAtLastStopTimer) / MICROS_PER_TICK; // adjust TickCounterForISR for correct gap value, which is used for repeat detection
        sMicrosAtLastStopTimer = 0;
    }
    timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us.
#ifdef _IR_MEASURE_TIMING
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
}
/**
 * Alias for start().
 */
void IRrecv::enableIRIn() {
    start();
}

/**
 * Configures the timer and the state machine for IR reception. Does not call resume()!
 * We assume, that timer interrupts are disabled here, otherwise it makes no sense to use this functions.
 * Therefore we do not need to guard the change of the volatile TickCounterForISR here :-).
 * The tick counter value is already at 100 when decode() gets true, because of the 5000 us minimal gap defined in RECORD_GAP_MICROS.
 * If TickCounterForISR is not adjusted with the value of the microseconds, the timer was stopped,
 * it can happen, that a new IR frame is recognized as a repeat, because the value of RECORD_GAP_MICROS
 * was not reached by TickCounterForISR counter before receiving the new IR frame.
 * @param aMicrosecondsToAddToGapCounter To compensate for the amount of microseconds the timer was stopped / disabled.
 */
void IRrecv::restartTimer(uint32_t aMicrosecondsToAddToGapCounter) {
    irparams.TickCounterForISR += aMicrosecondsToAddToGapCounter / MICROS_PER_TICK;
    timerConfigForReceive(); // no interrupts enabled here!
    timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us.
#ifdef _IR_MEASURE_TIMING
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
}
/**
 * Configures the timer and the state machine for IR reception. Does not call resume()!
 * @param aTicksToAddToGapCounter To compensate for the amount of ticks the timer was stopped / disabled.
 */
void IRrecv::restartTimerWithTicksToAdd(uint16_t aTicksToAddToGapCounter) {
    irparams.TickCounterForISR += aTicksToAddToGapCounter;
    timerConfigForReceive(); // no interrupts enabled here!
    timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us.
#ifdef _IR_MEASURE_TIMING
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
}
#if defined(ESP8266) || defined(ESP32)
#pragma GCC diagnostic push
#endif

/**
 * Restarts receiver after send. Is a NOP if sending does not require a timer.
 */
void IRrecv::restartAfterSend() {
#if defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER)
    start();
#endif
}

/**
 * Disables the timer for IR reception.
 */
void IRrecv::stop() {
    timerDisableReceiveInterrupt();
}

/*
 * Stores microseconds of stop, to adjust TickCounterForISR in restartTimer()
 */
void IRrecv::stopTimer() {
    timerDisableReceiveInterrupt();
    sMicrosAtLastStopTimer = micros();
}
/**
 * Alias for stop().
 */
void IRrecv::disableIRIn() {
    stop();
}
/**
 * Alias for stop().
 */
void IRrecv::end() {
    stop();
}

/**
 * Restart the ISR (Interrupt Service Routine) state machine, to enable receiving of the next IR frame.
 * Internal counting of gap timing is independent of StateForISR and therefore independent of call time of resume().
 */
void IRrecv::resume() {
    // This check allows to call resume at arbitrary places or more than once
    if (irparams.StateForISR == IR_REC_STATE_STOP) {
        irparams.StateForISR = IR_REC_STATE_IDLE;
    }
}

/**
 * Is internally called by decode before calling decoders.
 * Must be used to setup data, if you call decoders manually.
 */
void IRrecv::initDecodedIRData() {

    if (irparams.OverflowFlag) {
        decodedIRData.flags = IRDATA_FLAGS_WAS_OVERFLOW;

        DEBUG_PRINT(F("Overflow happened, try to increase the \"RAW_BUFFER_LENGTH\" value of "));
        DEBUG_PRINT(RAW_BUFFER_LENGTH);
        DEBUG_PRINTLN(F(" with #define RAW_BUFFER_LENGTH=<biggerValue>"));

    } else {
        decodedIRData.flags = IRDATA_FLAGS_EMPTY;
        // save last protocol, command and address for repeat handling (where they are compared or copied back :-))
        lastDecodedProtocol = decodedIRData.protocol; // repeat patterns can be equal between protocols (e.g. NEC, Samsung and LG), so we must keep the original one
        lastDecodedCommand = decodedIRData.command;
        lastDecodedAddress = decodedIRData.address;

    }
    decodedIRData.protocol = UNKNOWN;
    decodedIRData.command = 0;
    decodedIRData.address = 0;
    decodedIRData.decodedRawData = 0;
    decodedIRData.numberOfBits = 0;
}

/**
 * @return true if available() is true or IR receiver has not received a mark/signal since last resume().
 */
bool IRrecv::isIdle() {
    return (irparams.StateForISR == IR_REC_STATE_IDLE || available());
}

/**
 * Returns true if IR receiver has received a complete IR frame (detected by timeout after last mark).
 */
bool IRrecv::available() {
    return (irparams.StateForISR == IR_REC_STATE_STOP);
}

/**
 * Returns pointer to IrReceiver.decodedIRData if IR receiver data is available, else nullptr.
 */
IRData* IRrecv::read() {
    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return nullptr;
    }
    if (decode()) {
        return &decodedIRData;
    } else {
        return nullptr;
    }
}

/**
 * The main decode function, attempts to decode the recently receive IR signal.
 * The set of decoders used is determined by active definitions of the DECODE_<PROTOCOL> macros.
 * Results of decoding are stored in IrReceiver.decodedIRData.* like e.g. IrReceiver.decodedIRData.command.
 * @return false if no IR receiver data available, true if data available.
 */
bool IRrecv::decode() {
    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return false;
    }

    /*
     * Support for old examples, which do not use the default IrReceiver instance
     */
    if (this != &IrReceiver) {
        decodedIRData.initialGapTicks = irparams.initialGapTicks;
        decodedIRData.rawlen = irparams.rawlen;
    }

    initDecodedIRData(); // sets IRDATA_FLAGS_WAS_OVERFLOW

    if (decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) {
        /*
         * Set OverflowFlag flag and return true here, to let the loop call resume or print raw data.
         */
        decodedIRData.protocol = UNKNOWN;
        return true;
    }

#if defined(DECODE_NEC) || defined(DECODE_ONKYO)
    TRACE_PRINTLN(F("Attempting NEC/Onkyo decode"));
    if (decodeNEC()) {
        return true;
    }
#endif

#if defined(DECODE_KASEIKYO)
    TRACE_PRINTLN(F("Attempting Panasonic/Kaseikyo decode"));
    if (decodeKaseikyo()) {
        return true;
    }
#endif

#if defined(DECODE_DENON)
    TRACE_PRINTLN(F("Attempting Denon/Sharp decode"));
    if (decodeDenon()) {
        return true;
    }
#endif

#if defined(DECODE_SONY)
    TRACE_PRINTLN(F("Attempting Sony decode"));
    if (decodeSony()) {
        return true;
    }
#endif

#if defined(DECODE_RC5) || defined(DECODE_MARANTZ)
#  if defined(DECODE_RC5) && !defined(DECODE_MARANTZ)
    TRACE_PRINTLN(F("Attempting RC5 decode"));
#  elif !defined(DECODE_RC5) && defined(DECODE_MARANTZ)
    TRACE_PRINTLN(F("Attempting Marantz decode"));
#  else
    TRACE_PRINTLN(F("Attempting RC5 and Marantz decode"));
#  endif
    if (decodeRC5()) {
        return true;
    }
#endif

#if defined(DECODE_RC6)
    TRACE_PRINTLN(F("Attempting RC6 decode"));
    if (decodeRC6()) {
        return true;
    }
#endif

#if defined(DECODE_LG)
    TRACE_PRINTLN(F("Attempting LG decode"));
    if (decodeLG()) {
        return true;
    }
#endif

#if defined(DECODE_JVC)
    TRACE_PRINTLN(F("Attempting JVC decode"));
    if (decodeJVC()) {
        return true;
    }
#endif

#if defined(DECODE_SAMSUNG)
    TRACE_PRINTLN(F("Attempting Samsung decode"));
    if (decodeSamsung()) {
        return true;
    }
#endif
    /*
     * Start of the exotic protocols
     */

#if defined(DECODE_BEO)
    TRACE_PRINTLN(F("Attempting Bang & Olufsen decode"));
    if (decodeBangOlufsen()) {
        return true;
    }
#endif

#if defined(DECODE_FAST)
    TRACE_PRINTLN(F("Attempting FAST decode"));
    if (decodeFAST()) {
        return true;
    }
#endif

#if defined(DECODE_WHYNTER)
    TRACE_PRINTLN(F("Attempting Whynter decode"));
    if (decodeWhynter()) {
        return true;
    }
#endif

#if defined(DECODE_LEGO_PF)
    TRACE_PRINTLN(F("Attempting Lego Power Functions"));
    if (decodeLegoPowerFunctions()) {
        return true;
    }
#endif

#if defined(DECODE_BOSEWAVE)
    TRACE_PRINTLN(F("Attempting Bosewave decode"));
    if (decodeBoseWave()) {
        return true;
    }
#endif

#if defined(DECODE_MAGIQUEST)
    TRACE_PRINTLN(F("Attempting MagiQuest decode"));
    if (decodeMagiQuest()) {
        return true;
    }
#endif

#if defined(DECODE_OPENLASIR)
    TRACE_PRINTLN(F("Attempting OpenLASIR decode"));
    if (decodeOpenLASIR()) {
        return true;
    }
#endif

    /*
     * Try the universal decoder for pulse distance protocols
     */
#if defined(DECODE_DISTANCE_WIDTH)
    TRACE_PRINTLN(F("Attempting universal Distance Width decode"));
    if (decodeDistanceWidth()) {
        return true;
    }
#endif

    /*
     * Last resort is the universal hash decode which always return true
     */
#if defined(DECODE_HASH)
    TRACE_PRINTLN(F("Hash decode"));
    // decodeHash returns a hash on any input.
    // Thus, it needs to be last in the list.
    // If you add any decodes, add them before this.
    if (decodeHash()) {
        return true;
    }
#endif

    /*
     * Return true here, to let the loop decide to call resume or to print raw data.
     */
    return true;
}

/**********************************************************************************************************************
 * Common decode functions
 **********************************************************************************************************************/
/**
 * Decode pulse distance width protocols. We only check the mark or space length of a 1 against a threshold value, otherwise we always assume a 0!
 * We do not check the length of the constant pulse for the widely used PulseDistance width and the constant pause for PulseWidth (currently only sony protocol).
 * using matchMark() and matchSpace(), which includes MARK_EXCESS_MICROS in comparison.
 *
 * We can have the following protocol timings
 * PULSE_DISTANCE:       Pause/spaces have different length and determine the bit value, longer space is 1. Pulses/marks can be constant, like NEC.
 * PULSE_WIDTH:          Pulses/marks have different length and determine the bit value, longer mark is 1. Pause/spaces can be constant, like Sony.
 * PULSE_DISTANCE_WIDTH: Pulses/marks and pause/spaces have different length, often the bit length is constant, like MagiQuest. Can be decoded using the PULSE_DISTANCE decoding.
 *
 * Input is     IrReceiver.irparams.rawbuf[]
 * Output is    IrReceiver.decodedIRData.decodedRawData
 *
 * @param   aNumberOfBits           Number of bits to decode from irparams.rawbuf[] array.
 * @param   aStartOffset            Offset in irparams.rawbuf[] to start decoding. Must point to a mark.
 * @param   aOneMicros              Timing value for 1
 * @param   aIsPulseWidthProtocol   If true the aOneThresholdMicros id the mark threshold for 1, otherwise the space threshold
 * @param   aMSBfirst               If true send Most Significant Bit first, else send Least Significant Bit (lowest bit) first.
 */
void IRrecv::decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMicros,
        bool aIsPulseWidthProtocol, bool aMSBfirst) {

    DEBUG_PRINT(F("aOneMicros="));
    DEBUG_PRINT(aOneMicros);
    DEBUG_PRINT(F(", 0.75*aOneMicros="));
    DEBUG_PRINT((aOneMicros * 3) / 4);
    DEBUG_PRINT(F(", MARK_EXCESS_MICROS=" STR(MARK_EXCESS_MICROS) " isPulseWidthProtocol="));
    DEBUG_PRINT(aIsPulseWidthProtocol);
    DEBUG_PRINTLN();

    IRDecodedRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop
    IRDecodedRawDataType tMask = 1UL; // Mask is only used for LSB first
    auto *tRawBufPointer = &irparams.rawbuf[aStartOffset];

    for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {

        bool tBitValue;
        uint16_t tCurrentTicks;
        if (aIsPulseWidthProtocol) {
            /*
             * PULSE_WIDTH here.
             * !!!We only check variable length mark indicating a 1 or 0!!!
             */
            tCurrentTicks = *tRawBufPointer++;
            tBitValue = matchMark(tCurrentTicks, aOneMicros); // Check for variable length mark indicating a 1 or 0
            tRawBufPointer++;
        } else {
            /*
             * PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- here.
             * !!!We only check variable length space indicating a 1 or 0!!!
             */
            tRawBufPointer++;
            tCurrentTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-)
            tBitValue = matchSpace(tCurrentTicks, aOneMicros); // Check for variable length space indicating a 1 or 0
        }

        if (aMSBfirst) {
            tDecodedData <<= 1;
        }

        if (tBitValue) {
            // It's a 1 -> set the bit
            if (aMSBfirst) {
                tDecodedData |= 1;
            } else {
                tDecodedData |= tMask;
            }
            TRACE_PRINT(tCurrentTicks);
            TRACE_PRINTLN(F(" => 1"));
        } else {
            // do not set the bit
            TRACE_PRINT(tCurrentTicks);
            TRACE_PRINTLN(F(" => 0"));
        }
        tMask <<= 1;
    }
    decodedIRData.decodedRawData = tDecodedData;
}

/**
 * New threshold decoder to be activated by USE_THRESHOLD_DECODER
 * Assumes a 0 for shorter and a 1 for longer timing. Thus the value must be eventually inverted by the calling protocol decoder (MagiQuest).
 * In normal decoder, we check whether there is a timing for a 1 (usually the longer timing) within a range of 75 % to 125 % of the nominal timing. See TICKS_LOW.
 * Many protocols have a ratio of 1 to 3 between short and long timing. In this case this decoder extends the range to 66% and the upper bound does not matter anyway.
 * I hope that this will give slightly better results especially for
 *  - jittering signals
 *  - protocols with a smaller timing ratio than 1 to 2
 *  - protocols with short 1 pulses / pauses like MagiQuest.
 * Requires up to 458 bytes more program space, if more than 1 protocol is involved.
 * but saves 90 bytes if only e.g. NEC is selected.
 *
 * @param   aNumberOfBits           Number of bits to decode from irparams.rawbuf[] array.
 * @param   aStartOffset            Offset in irparams.rawbuf[] to start decoding. Must point to a mark.
 * @param   aOneThresholdMicros     Threshold value for 1
 * @param   aIsPulseWidthProtocol   If true the aOneThresholdMicros id the mark threshold for 1, otherwise the space threshold
 * @param   aMSBfirst               If true send Most Significant Bit first, else send Least Significant Bit (lowest bit) first.
 */
void IRrecv::decodeWithThresholdPulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset,
        uint16_t aOneThresholdMicros, bool aIsPulseWidthProtocol, bool aMSBfirst) {

    DEBUG_PRINT(F("OneThresholdMicros="));
    DEBUG_PRINT(aOneThresholdMicros);
    DEBUG_PRINT(F(" isPulseWidthProtocol="));
    DEBUG_PRINT(aIsPulseWidthProtocol);
    DEBUG_PRINTLN();

    IRDecodedRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop
    IRDecodedRawDataType tMask = 1UL; // Mask is only used for LSB first
    auto *tRawBufPointer = &irparams.rawbuf[aStartOffset];

    for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {

        if (!aIsPulseWidthProtocol) {
            tRawBufPointer++;
        }
        uint16_t tCurrentMicros = *tRawBufPointer * MICROS_PER_TICK;
        bool tBitValue = tCurrentMicros > aOneThresholdMicros; // Check for variable length timing indicating a 1 or 0
        tRawBufPointer++;

        if (aIsPulseWidthProtocol) {
            tRawBufPointer++;
        }

        if (aMSBfirst) {
            tDecodedData <<= 1;
        }

        if (tBitValue) {
            // It's a 1 -> set the bit
            if (aMSBfirst) {
                tDecodedData |= 1;
            } else {
                tDecodedData |= tMask;
            }
            TRACE_PRINT(tCurrentMicros);
            TRACE_PRINTLN(F(" => 1"));
        } else {
            // do not set the bit
            TRACE_PRINT(tCurrentMicros);
            TRACE_PRINTLN(F(" => 0"));
        }
        tMask <<= 1;
    }
    decodedIRData.decodedRawData = tDecodedData;
}

/**
 * Old deprecated version with 3 timing parameters instead of one and a aIsPulseWidthProtocol flag
 */
void IRrecv::decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
        uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, bool aMSBfirst) {

    auto *tRawBufPointer = &irparams.rawbuf[aStartOffset];

    bool isPulseDistanceProtocol = (aOneMarkMicros == aZeroMarkMicros); // If true, we check aOneSpaceMicros -> pulse distance protocol

    IRDecodedRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop
    IRDecodedRawDataType tMask = 1UL; // Mask is only used for LSB first

    for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {
        // get one mark and space pair
        unsigned int tMarkTicks;
        unsigned int tSpaceTicks;
        bool tBitValue;

        if (isPulseDistanceProtocol) {
            /*
             * PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- here.
             * !!!We only check variable length space indicating a 1 or 0!!!
             */
            tRawBufPointer++;
            tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-)
            tBitValue = matchSpace(tSpaceTicks, aOneSpaceMicros); // Check for variable length space indicating a 1 or 0

        } else {
            /*
             * PULSE_WIDTH here.
             * !!!We only check variable length mark indicating a 1 or 0!!!
             */
            tMarkTicks = *tRawBufPointer++;
            tBitValue = matchMark(tMarkTicks, aOneMarkMicros); // Check for variable length mark indicating a 1 or 0
            tRawBufPointer++;
        }

        if (aMSBfirst) {
            tDecodedData <<= 1;
        }

        if (tBitValue) {
            // It's a 1 -> set the bit
            if (aMSBfirst) {
                tDecodedData |= 1;
            } else {
                tDecodedData |= tMask;
            }
            TRACE_PRINTLN(F("=> 1"));
        } else {
            // do not set the bit
            TRACE_PRINTLN(F("=> 0"));
        }
        tMask <<= 1;
    }
    decodedIRData.decodedRawData = tDecodedData;
}

/*
 * Old deprecated version with 7 parameters and unused aZeroSpaceMicros parameter
 */
void IRrecv::decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
        uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst) {

    (void) aZeroSpaceMicros;
    decodePulseDistanceWidthData(aNumberOfBits, aStartOffset, aOneMarkMicros, aZeroMarkMicros, aOneSpaceMicros, aMSBfirst);
}

/*
 * Only sensible for development or very exotic requirements. - Not used yet
 * Check for additional required characteristics of timing like length of mark for a constant mark protocol,
 * where space length determines the bit value. Requires up to 194 additional bytes of program memory.
 *
 * @param   aZeroMarkMicros     For strict checks
 * @param   aZeroSpaceMicros    For strict checks
 * @return  true if decoding successful
 *
 */
bool IRrecv::decodeStrictPulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
        uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst) {

    auto *tRawBufPointer = &irparams.rawbuf[aStartOffset];

    bool isPulseDistanceProtocol = (aOneMarkMicros == aZeroMarkMicros); // If true, we have a constant mark -> pulse distance protocol

    IRDecodedRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop
    IRDecodedRawDataType tMask = 1UL; // Mask is only used for LSB first

    for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {
        // get one mark and space pair
        unsigned int tMarkTicks;
        unsigned int tSpaceTicks;
        bool tBitValue;

        if (isPulseDistanceProtocol) {
            /*
             * PULSE_DISTANCE here (aOneMarkMicros == aZeroMarkMicros)
             * We additionally check constant mark duration and in case of zero the zero space duration.
             */
            tMarkTicks = *tRawBufPointer++;
            tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-)
            tBitValue = matchSpace(tSpaceTicks, aOneSpaceMicros); // Check for variable length space indicating a 1 or 0

            // Check for constant mark duration
            if (!matchMark(tMarkTicks, aOneMarkMicros)) {
                DEBUG_PRINT(F("Mark="));
                DEBUG_PRINT(tMarkTicks * MICROS_PER_TICK);
                DEBUG_PRINT(F(" is not "));
                DEBUG_PRINT(aOneMarkMicros);
                DEBUG_PRINT(F(". Index="));
                DEBUG_PRINT(aNumberOfBits - i);
                DEBUG_PRINT(' ');
                return false;
            }

            // in case of 0, check for zero space duration
            if (!tBitValue && !matchSpace(tSpaceTicks, aZeroSpaceMicros)) {
                DEBUG_PRINT(F("Space="));
                DEBUG_PRINT(tSpaceTicks * MICROS_PER_TICK);
                DEBUG_PRINT(F(" is not "));
                DEBUG_PRINT(aZeroSpaceMicros);
                DEBUG_PRINT(F(". Index="));
                DEBUG_PRINT(aNumberOfBits - i);
                DEBUG_PRINT(' ');
                return false;
            }

        } else {
            /*
             * PULSE_WIDTH -including PULSE_DISTANCE_WIDTH-  here.
             * We additionally check two space durations and in case of zero the zero mark duration.
             */
            tMarkTicks = *tRawBufPointer++;
            tBitValue = matchMark(tMarkTicks, aOneMarkMicros); // Check for variable length mark indicating a 1 or 0
            tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-)

            // Check for space length, which is not constant in case of PULSE_DISTANCE_WIDTH
            if ((tBitValue && !matchSpace(tSpaceTicks, aOneSpaceMicros))
                    || ((!tBitValue && !matchSpace(tSpaceTicks, aZeroSpaceMicros)))) {
                DEBUG_PRINT(F("Space="));
                DEBUG_PRINT(tSpaceTicks * MICROS_PER_TICK);
                DEBUG_PRINT(F(" is not "));
                DEBUG_PRINT(aOneSpaceMicros);
                DEBUG_PRINT(F(" or "));
                DEBUG_PRINT(aZeroSpaceMicros);
                DEBUG_PRINT(F(". Index="));
                DEBUG_PRINT(aNumberOfBits - i);
                DEBUG_PRINT(' ');
                return false;
            }
            if (!tBitValue && !matchMark(tMarkTicks, aZeroMarkMicros)) {
                DEBUG_PRINT(F("Mark="));
                DEBUG_PRINT(tMarkTicks * MICROS_PER_TICK);
                DEBUG_PRINT(F(" is not "));
                DEBUG_PRINT(aZeroMarkMicros);
                DEBUG_PRINT(F(". Index="));
                DEBUG_PRINT(aNumberOfBits - i);
                DEBUG_PRINT(' ');
                return false;
            }
        }

        if (aMSBfirst) {
            tDecodedData <<= 1;
        }

        if (tBitValue) {
            // It's a 1 -> set the bit
            if (aMSBfirst) {
                tDecodedData |= 1;
            } else {
                tDecodedData |= tMask;
            }
            TRACE_PRINTLN(F("=> 1"));
        } else {
            // do not set the bit
            TRACE_PRINTLN(F("=> 0"));
        }
        tMask <<= 1;
    }
    decodedIRData.decodedRawData = tDecodedData;
    return true;
}

/**
 * Decode pulse distance protocols for PulseDistanceWidthProtocolConstants.
 * @return  true if decoding was successful
 */
#if defined(USE_STRICT_DECODER)
bool
#else
void
#endif
IRrecv::decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits,
        IRRawlenType aStartOffset) {

#if defined(USE_STRICT_DECODER)
        return decodeStrictPulseDistanceWidthData(aNumberOfBits, aStartOffset, aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros,
                aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros,
                aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, (aProtocolConstants->Flags & PROTOCOL_IS_MSB_MASK));

#else
    bool tIsPulseWidthProtocol = aProtocolConstants->Flags & PROTOCOL_IS_PULSE_WIDTH_MASK;
    uint16_t tThresholdMicros;

#  if defined(USE_THRESHOLD_DECODER)
        if (tIsPulseWidthProtocol) {
            // we check the length of marks here
            tThresholdMicros = ((aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros
                            + aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros) / 2) - MARK_EXCESS_MICROS;// MARK_EXCESS_MICROS is 0 here if not explicitly specified by user
        } else {
            // we check the length of spaces here
            tThresholdMicros = ((aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros
                            + aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros) / 2) + MARK_EXCESS_MICROS;// MARK_EXCESS_MICROS is 0 here if not explicitly specified by user
        }
        return decodeWithThresholdPulseDistanceWidthData(aNumberOfBits, aStartOffset, tThresholdMicros,
                aProtocolConstants->Flags & PROTOCOL_IS_PULSE_WIDTH_MASK, (aProtocolConstants->Flags & PROTOCOL_IS_MSB_MASK));
#  else
    if (tIsPulseWidthProtocol) {
        tThresholdMicros = aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros;
    } else {
        tThresholdMicros = aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros;
    }
    decodePulseDistanceWidthData(aNumberOfBits, aStartOffset, tThresholdMicros,
            aProtocolConstants->Flags & PROTOCOL_IS_PULSE_WIDTH_MASK, (aProtocolConstants->Flags & PROTOCOL_IS_MSB_MASK));
#  endif
#endif
}

void IRrecv::decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM,
        uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset) {

    PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants;
    memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM,
            sizeof(tTemporaryPulseDistanceWidthProtocolConstants));

    decodePulseDistanceWidthData(&tTemporaryPulseDistanceWidthProtocolConstants, aNumberOfBits, aStartOffset);
}

/**********************************************************************************************************************
 * Internal Hash decode function
 **********************************************************************************************************************/
#define FNV_PRIME_32 16777619   ///< used for decodeHash()
#define FNV_BASIS_32 2166136261 ///< used for decodeHash()

/**
 * Compare two (tick) values for Hash decoder
 * Use a tolerance of 20% to enable e.g. 500 and 600 (NEC timing) and at least 200 and 250 to be equal
 * @return  0 if newval is shorter, 1 if newval is equal, and 2 if newval is longer
 */
uint_fast8_t IRrecv::compare(uint16_t oldval, uint16_t newval) {
    if (newval * 10 < oldval * 8) {
        return 0;
    }
    if (oldval * 10 < newval * 8) {
        return 2;
    }
    return 1;
}

/**
 * Decodes an arbitrary IR code to a 32-bit value.
 * Instead of decoding using a standard encoding scheme
 * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value.
 *
 * The algorithm looks at the sequence of MARK and SPACE signals, and see if each one
 * is more than 20% shorter (0), the same (+/- 20%) length (1), or more than 20% longer (2) than the previous MARK or SPACE.
 * It hash the resulting sequence of 0's, 1's, and 2's to a 32-bit value.
 * This will give a unique value for each different code (probably), for most code systems.
 *
 * Uses FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
 * Converts the raw code values into a 32-bit hash code.
 * Hopefully this code is unique for each button.
 * This isn't a "real" decoding, just an arbitrary value.
 *
 * see: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html
 */
bool IRrecv::decodeHash() {
    unsigned long hash = FNV_BASIS_32; // the result is the same no matter if we use a long or unsigned long variable

// Require at least 6 samples to prevent triggering on noise
    if (decodedIRData.rawlen < 6) {
        DEBUG_PRINT(F("HASH: Data length="));
        DEBUG_PRINT(decodedIRData.rawlen);
        DEBUG_PRINTLN(F(" is less than 6"));
        return false;
    }
    for (IRRawlenType i = 1; (i + 2) < decodedIRData.rawlen; i++) {
        // Compare mark with mark and space with space
        uint_fast8_t value = compare(irparams.rawbuf[i], irparams.rawbuf[i + 2]);
        // Add value into the hash - (0 if rawbuf[i + 2] is more than 20 % shorter, 1 if rawbuf[i + 2] is equal, and 2 if rawbuf[i + 2] is longer than rawbuf[i])
        hash = (hash * FNV_PRIME_32) ^ value;
    }

    decodedIRData.decodedRawData = hash;
    decodedIRData.numberOfBits = 32;
    decodedIRData.protocol = UNKNOWN;

    return true;
}

bool IRrecv::decodeHashOld(decode_results *aResults) {
    unsigned long hash = FNV_BASIS_32;

// Require at least 6 samples to prevent triggering on noise
    if (aResults->rawlen < 6) {
        return false;
    }

    for (uint8_t i = 3; i < aResults->rawlen; i++) {
        uint_fast8_t value = compare(aResults->rawbuf[i - 2], aResults->rawbuf[i]);
        // Add value into the hash
        hash = (hash * FNV_PRIME_32) ^ value;
    }

    aResults->value = hash;
    aResults->bits = 32;
    aResults->decode_type = UNKNOWN;
    decodedIRData.protocol = UNKNOWN;

    return true;
}

/**********************************************************************************************************************
 * Match functions
 **********************************************************************************************************************/

/*
 * returns true if values do match
 */
bool IRrecv::checkHeader(PulseDistanceWidthProtocolConstants *aProtocolConstants) {
// Check header "mark" and "space"
    if (!matchMark(irparams.rawbuf[1], aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros)) {
        TRACE_PRINT(::getProtocolString(aProtocolConstants->ProtocolIndex));
        TRACE_PRINTLN(F(": Header mark length is wrong"));
        return false;
    }
    if (!matchSpace(irparams.rawbuf[2], aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros)) {
        TRACE_PRINT(::getProtocolString(aProtocolConstants->ProtocolIndex));
        TRACE_PRINTLN(F(": Header space length is wrong"));
        return false;
    }
    return true;
}

bool IRrecv::checkHeader_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM) {
// Check header "mark" and "space"
    if (!matchMark(irparams.rawbuf[1], pgm_read_word(&aProtocolConstantsPGM->DistanceWidthTimingInfo.HeaderMarkMicros))) {
        TRACE_PRINT(::getProtocolString((decode_type_t ) pgm_read_byte(&aProtocolConstantsPGM->ProtocolIndex)));
        TRACE_PRINTLN(F(": Header mark length is wrong"));
        return false;
    }
    if (!matchSpace(irparams.rawbuf[2], pgm_read_word(&aProtocolConstantsPGM->DistanceWidthTimingInfo.HeaderSpaceMicros))) {
        TRACE_PRINT(::getProtocolString((decode_type_t ) pgm_read_byte(&aProtocolConstantsPGM->ProtocolIndex)));
        TRACE_PRINTLN(F(": Header space length is wrong"));
        return false;
    }
    return true;
}

/*
 * Does not check for same address and command, because it is almost not possible to press 2 different buttons on the remote within around 100 ms.
 * And if really required, it can be enabled here, or done manually in user program.
 * And we have still no RC6 toggle bit check for detecting a second press on the same button.
 */
void IRrecv::checkForRepeatSpaceTicksAndSetFlag(uint16_t aMaximumRepeatSpaceTicks) {
    if (decodedIRData.initialGapTicks < aMaximumRepeatSpaceTicks
#if defined(ENABLE_COMPLETE_REPEAT_CHECK)
// Check also for same command and address values to detect a repeat. Not sensible for standard protocols, because it is almost not possible to press 2 different buttons on the remote within around 100 ms
                && decodedIRData.address == lastDecodedAddress && decodedIRData.command == lastDecodedCommand /* requires around 44 bytes program space */
#endif
            ) {
        decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
    }
}

/**
 * Match function WITHOUT compensating for marks exceeded or spaces shortened by demodulator hardware
 * Match is true, if signal value is bigger/equal 75% and less/equal 125% of aMatchValueMicros.
 * @return true, if values match
 */
bool matchTicks(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
    uint16_t tMeasuredMicros = (aMeasuredTicks * MICROS_PER_TICK);
    uint16_t tMatchValueMicrosQuarter = aMatchValueMicros / 4;

    TRACE_PRINT(F("Testing (actual vs desired): "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F("us vs "));
    TRACE_PRINT(aMatchValueMicros);
    TRACE_PRINT(F("us: "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 3); // rounded value because we divide first
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 5);

    bool passed = (tMeasuredMicros >= (tMatchValueMicrosQuarter * 3) && tMeasuredMicros <= (tMatchValueMicrosQuarter * 5));
#if defined(LOCAL_TRACE)
    if (passed) {
        Serial.println(F(" => passed"));
    } else {
        Serial.println(F(" => FAILED"));
    }
#endif
    return passed;
}

bool matchTicksWithGreaterRange(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
    uint16_t tMeasuredMicros = (aMeasuredTicks * MICROS_PER_TICK);
    uint16_t tMatchValueMicrosQuarter = aMatchValueMicros / 4;

    TRACE_PRINT(F("Testing with greater range (actual vs desired): "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F("us vs "));
    TRACE_PRINT(aMatchValueMicros);
    TRACE_PRINT(F("us: "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 2); // rounded value because we divide first
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 6);

    bool passed = (tMeasuredMicros >= (tMatchValueMicrosQuarter * 2) && tMeasuredMicros <= (tMatchValueMicrosQuarter * 6));
#if defined(LOCAL_TRACE)
    if (passed) {
        Serial.println(F(" => passed"));
    } else {
        Serial.println(F(" => FAILED"));
    }
#endif
    return passed;
}
/**
 * Match function WITH compensating for marks exceeded or spaces shortened by demodulator hardware
 * With MARK_EXCESS_MICROS default value of 20 we cannot match 200 to 250, because we have 186 < 180 <= 310
 * @return true, if values match
 */
bool matchTicks(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros, int16_t aCompensationMicrosForTicks) {
    uint16_t tMeasuredMicros = (aMeasuredTicks * MICROS_PER_TICK) + aCompensationMicrosForTicks;
    uint16_t tMatchValueMicrosQuarter = aMatchValueMicros / 4;

    TRACE_PRINT(F("Testing (actual vs desired): "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F("us vs "));
    TRACE_PRINT(aMatchValueMicros);
    TRACE_PRINT(F("us: "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 3); // rounded value because we divide first
    TRACE_PRINT(F(" < "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 5);

    bool passed = (tMeasuredMicros > (tMatchValueMicrosQuarter * 3) && tMeasuredMicros <= (tMatchValueMicrosQuarter * 5));
#if defined(LOCAL_TRACE)
    if (passed) {
        Serial.println(F(" => passed"));
    } else {
        Serial.println(F(" => FAILED"));
    }
#endif
    return passed;
}

bool matchTicksWithGreaterRange(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros, int16_t aCompensationMicrosForTicks) {
    uint16_t tMeasuredMicros = (aMeasuredTicks * MICROS_PER_TICK) + aCompensationMicrosForTicks;
    uint16_t tMatchValueMicrosQuarter = aMatchValueMicros / 4;

    TRACE_PRINT(F("Testing with greater range (actual vs desired): "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F("us vs "));
    TRACE_PRINT(aMatchValueMicros);
    TRACE_PRINT(F("us: "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 2); // rounded value because we divide first
    TRACE_PRINT(F(" < "));
    TRACE_PRINT(tMeasuredMicros);
    TRACE_PRINT(F(" <= "));
    TRACE_PRINT(tMatchValueMicrosQuarter * 6);

    bool passed = (tMeasuredMicros > (tMatchValueMicrosQuarter * 2) && tMeasuredMicros <= (tMatchValueMicrosQuarter * 6));
#if defined(LOCAL_TRACE)
    if (passed) {
        Serial.println(F(" => passed"));
    } else {
        Serial.println(F(" => FAILED"));
    }
#endif
    return passed;
}

bool MATCH(uint16_t measured_ticks, uint16_t desired_us) {
    return matchTicks(measured_ticks, desired_us);
}

/**
 * Compensate for marks exceeded by demodulator hardware
 * @return true, if values match
 */
bool matchMark(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
#if (MARK_EXCESS_MICROS == 0)
    return matchTicks(aMeasuredTicks, aMatchValueMicros);
#else

#  if !defined(USE_OLD_MATCH_FUNCTIONS)
        return matchTicks(aMeasuredTicks, aMatchValueMicros, -MARK_EXCESS_MICROS); // New handling of MARK_EXCESS_MICROS without strange rounding errors
#  endif

        // old version here
        aMatchValueMicros += MARK_EXCESS_MICROS;

        TRACE_PRINT(F("Testing mark (actual vs desired): "));
        TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
        TRACE_PRINT(F("us vs "));
        TRACE_PRINT(aMatchValueMicros);
        TRACE_PRINT(F("us: "));
//    TRACE_PRINT(F("TICKS_LOW="));
//    TRACE_PRINT(TICKS_LOW(aMatchValueMicros));
//    TRACE_PRINT(F(" "));
        TRACE_PRINT(TICKS_LOW(aMatchValueMicros) * MICROS_PER_TICK);
        TRACE_PRINT(F(" <= "));
        TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
        TRACE_PRINT(F(" <= "));
        TRACE_PRINT(TICKS_HIGH(aMatchValueMicros) * MICROS_PER_TICK);

        // compensate for marks exceeded by demodulator hardware
        bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros))
                && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros)));
#  if defined(LOCAL_TRACE)
        if (passed) {
            Serial.println(F(" => passed"));
        } else {
            Serial.println(F(" => FAILED"));
        }
#  endif
        return passed;
#endif
}

bool matchMarkWithGreaterRange(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
#if (MARK_EXCESS_MICROS == 0)
    return matchTicksWithGreaterRange(aMeasuredTicks, aMatchValueMicros);
#else
    return matchTicksWithGreaterRange(aMeasuredTicks, aMatchValueMicros, -MARK_EXCESS_MICROS); // New handling of MARK_EXCESS_MICROS without strange rounding errors
#endif
}

bool MATCH_MARK(uint16_t measured_ticks, uint16_t desired_us) {
    return matchMark(measured_ticks, desired_us);
}

/**
 * Compensate for spaces shortened by demodulator hardware
 * @return true, if values match
 */
bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
#if (MARK_EXCESS_MICROS == 0)
    return matchTicks(aMeasuredTicks, aMatchValueMicros);
#else
#  if !defined(USE_OLD_MATCH_FUNCTIONS)
        return matchTicks(aMeasuredTicks, aMatchValueMicros, MARK_EXCESS_MICROS); // New handling of MARK_EXCESS_MICROS without strange rounding errors
#  endif
        // old version here
        aMatchValueMicros -= MARK_EXCESS_MICROS;

        TRACE_PRINT(F("Testing space (actual vs desired): "));
        TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
        TRACE_PRINT(F("us vs "));
        TRACE_PRINT(aMatchValueMicros);
        TRACE_PRINT(F("us: "));
        TRACE_PRINT(F("LOW="));
        TRACE_PRINT(TICKS_LOW(aMatchValueMicros));
        TRACE_PRINT(F(" "));
        TRACE_PRINT(TICKS_LOW(aMatchValueMicros) * MICROS_PER_TICK);
        TRACE_PRINT(F(" <= "));
        TRACE_PRINT(aMeasuredTicks * MICROS_PER_TICK);
        TRACE_PRINT(F(" <= "));
        TRACE_PRINT(TICKS_HIGH(aMatchValueMicros) * MICROS_PER_TICK);

        // compensate for spaces shortened by demodulator hardware
        bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros))
                && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros)));
#  if defined(LOCAL_TRACE)
        if (passed) {
            Serial.println(F(" => passed"));
        } else {
            Serial.println(F(" => FAILED"));
        }
#  endif
        return passed;
#endif
}

bool matchSpaceWithGreaterRange(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
#if (MARK_EXCESS_MICROS == 0)
    return matchTicksWithGreaterRange(aMeasuredTicks, aMatchValueMicros);
#else
    return matchTicksWithGreaterRange(aMeasuredTicks, aMatchValueMicros, MARK_EXCESS_MICROS); // New handling of MARK_EXCESS_MICROS without strange rounding errors
#endif
}

bool MATCH_SPACE(uint16_t measured_ticks, uint16_t desired_us) {
    return matchSpace(measured_ticks, desired_us);
}

/**
 * Getter function for MARK_EXCESS_MICROS
 */
int getMarkExcessMicros() {
    return MARK_EXCESS_MICROS;
}

/**
 * Checks if protocol is not detected and detected space between two transmissions
 * is smaller than known value for protocols (Sony with around 24 ms)
 * @return true, if CheckForRecordGapsMicros() has printed a message, i.e. gap < 15ms (RECORD_GAP_MICROS_WARNING_THRESHOLD)
 */
bool IRrecv::checkForRecordGapsMicros(Print *aSerial) {
    /*
     * Check if protocol is not detected and detected space between two transmissions
     * is smaller than known value for protocols (Sony with around 24 ms)
     */
    if (decodedIRData.protocol <= PULSE_DISTANCE
            && decodedIRData.initialGapTicks < (RECORD_GAP_MICROS_WARNING_THRESHOLD / MICROS_PER_TICK)) {
        aSerial->println();
        aSerial->print(F("Space of "));
        aSerial->print(decodedIRData.initialGapTicks * MICROS_PER_TICK);
        aSerial->print(F(" us between two detected transmission is smaller than the minimal gap of "));
        aSerial->print(RECORD_GAP_MICROS_WARNING_THRESHOLD);
        aSerial->println(F(" us known for implemented protocols like NEC, Sony, RC5 etc.."));
        aSerial->println(F("But it can be OK for some yet unsupported protocols, and especially for repeats."));
        aSerial->println(F("If you get unexpected results, try to increase the RECORD_GAP_MICROS in IRremote.h."));
        aSerial->println();
        return true;
    }
    return false;
}

/**********************************************************************************************************************
 * Print functions
 * Since a library should not allocate the "Serial" object, all functions require a pointer to a Print object.
 **********************************************************************************************************************/
void IRrecv::printActiveIRProtocols(Print *aSerial) {
// call no class function with same name
    ::printActiveIRProtocols(aSerial);
}
/*
 * Prints a list of enabled protocols for this application.
 * @param aSerial pointer to serial used for printing. Use "&Serial".
 */
void printActiveIRProtocols(Print *aSerial) {
#if defined(DECODE_ONKYO)
        aSerial->print(F("Onkyo, "));
#elif defined(DECODE_NEC)
        aSerial->print(F("NEC/NEC2/Onkyo/Apple, "));
#endif
#if defined(DECODE_KASEIKYO)
        aSerial->print(F("Panasonic/Kaseikyo, "));
#endif
#if defined(DECODE_DENON)
        aSerial->print(F("Denon/Sharp, "));
#endif
#if defined(DECODE_SONY)
        aSerial->print(F("Sony, "));
#endif
#if defined(DECODE_RC5)
        aSerial->print(F("RC5, "));
#endif
#if defined(DECODE_RC6)
        aSerial->print(F("RC6, "));
#endif
#if defined(DECODE_LG)
        aSerial->print(F("LG, "));
#endif
#if defined(DECODE_JVC)
        aSerial->print(F("JVC, "));
#endif
#if defined(DECODE_SAMSUNG)
        aSerial->print(F("Samsung, "));
#endif
    /*
     * Start of the exotic protocols
     */
#if defined(DECODE_BEO)
        aSerial->print(F("Bang & Olufsen, "));
#endif
#if defined(DECODE_FAST)
        aSerial->print(F("FAST, "));
#endif
#if defined(DECODE_WHYNTER)
        aSerial->print(F("Whynter, "));
#endif
#if defined(DECODE_LEGO_PF)
        aSerial->print(F("Lego Power Functions, "));
#endif
#if defined(DECODE_BOSEWAVE)
        aSerial->print(F("Bosewave, "));
#endif
#if defined(DECODE_MAGIQUEST)
        aSerial->print(F("MagiQuest, "));
#endif
#if defined(DECODE_OPENLASIR)
        aSerial->print(F("OpenLASIR, "));
#endif
#if defined(DECODE_DISTANCE_WIDTH)
        aSerial->print(F("Universal Pulse Distance Width, "));
#endif
#if defined(DECODE_HASH)
        aSerial->print(F("Hash "));
#endif
#if defined(NO_DECODER) // for sending raw only
        (void)aSerial; // to avoid compiler warnings
#endif
}

/** @}
 * \addtogroup Print Print functions
 * @{
 */

/**
 * Function to print values and flags of IrReceiver.decodedIRData in one line.
 * Ends with println().
 *
 * @param aSerial   The Print object on which to write, for Arduino you can use &Serial.
 * @param aCheckForRecordGapsMicros   If true, call CheckForRecordGapsMicros() which may do a long printout,
 *                                    which in turn may block the proper detection of repeats.*
 * @return true, if CheckForRecordGapsMicros() has printed a message, i.e. gap < 15ms (RECORD_GAP_MICROS_WARNING_THRESHOLD).
 */
bool IRrecv::printIRResultShort(Print *aSerial, bool aPrintRepeatGap, bool aCheckForRecordGapsMicros) {
    // DEPRECATED
    (void) aPrintRepeatGap;
    return printIRResultShort(aSerial, aCheckForRecordGapsMicros);
}
bool IRrecv::printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros) {

    uint8_t tFlags = decodedIRData.flags;
    if (tFlags & IRDATA_FLAGS_WAS_OVERFLOW) {
        aSerial->println(F("Overflow"));
    } else {
        aSerial->print(F("Protocol="));
        aSerial->print(getProtocolString());
        if (decodedIRData.protocol == UNKNOWN) {
#if defined(DECODE_HASH)
            aSerial->print(F(" Hash=0x"));
#if (__INT_WIDTH__ < 32)
            aSerial->print(decodedIRData.decodedRawData, HEX);
#else
            PrintULL::print(aSerial,decodedIRData.decodedRawData, HEX);
#endif

#endif
#if !defined(DISABLE_CODE_FOR_RECEIVER)
            aSerial->print(' ');
            aSerial->print((decodedIRData.rawlen + 1) / 2);
            aSerial->println(F(" bits (incl. gap and start) received"));
#endif
        } else {
#if defined(DECODE_DISTANCE_WIDTH)
                if (decodedIRData.protocol != PULSE_DISTANCE && decodedIRData.protocol != PULSE_WIDTH) {
#endif
            /*
             * New decoders have address and command
             */
            aSerial->print(F(" Address=0x"));
            aSerial->print(decodedIRData.address, HEX);

            aSerial->print(F(", Command=0x"));
            aSerial->print(decodedIRData.command, HEX);

            if (tFlags & IRDATA_FLAGS_EXTRA_INFO) {
                aSerial->print(F(", Extra=0x"));
                aSerial->print(decodedIRData.extra, HEX);
            }

            if (tFlags & IRDATA_FLAGS_PARITY_FAILED) {
                aSerial->print(F(", Parity fail"));
            }

            if (tFlags & IRDATA_FLAGS_TOGGLE_BIT) {
                aSerial->print(F(", Toggle=1"));
            }
#if defined(DECODE_DISTANCE_WIDTH)
                }
#endif

            /*
             * Print raw data, numberOfBits and MSB or LSB
             */
            if (!(tFlags & IRDATA_FLAGS_IS_REPEAT) || decodedIRData.decodedRawData != 0) {
                aSerial->print(F(", Raw-Data=0x"));
#if (__INT_WIDTH__ < 32)
                aSerial->print(decodedIRData.decodedRawData, HEX);
#else
                PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
#endif
                aSerial->print(F(", "));

                /*
                 * Print number of bits processed
                 */
                aSerial->print(decodedIRData.numberOfBits);
                aSerial->print(F(" bits,"));

                if (tFlags & IRDATA_FLAGS_IS_MSB_FIRST) {
                    aSerial->print(F(" MSB first"));
                } else {
                    aSerial->print(F(" LSB first"));
                }
            }

            /*
             * Print gap and duration, in order to be able to compute the repeat period of the protocol by adding the next gap time
             */
            if (tFlags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
                aSerial->print(F(", "));
                if (tFlags & IRDATA_FLAGS_IS_AUTO_REPEAT) {
                    aSerial->print(F("Auto-"));
                }
                aSerial->print(F("Repeat"));
            }
#if !defined(DISABLE_CODE_FOR_RECEIVER)
            aSerial->print(F(", Gap="));
            aSerial->print((uint32_t) decodedIRData.initialGapTicks * MICROS_PER_TICK);
            aSerial->print(F("us"));

            /*
             * Print complete duration of IR frame
             */
            uint16_t tSumOfDurationTicks = 0;
            for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) {
                tSumOfDurationTicks += irparams.rawbuf[i];
            }
            aSerial->print(F(", Duration="));
            aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK);
            aSerial->println(F("us"));
#else
                aSerial->println();
#endif
        }
    }

// call no class function with same name
    if (aCheckForRecordGapsMicros && decodedIRData.protocol != UNKNOWN) {
        return checkForRecordGapsMicros(aSerial);
    }
    return false;
}

/*
 * Only used in example ReceiveAndSend.cpp
 */
void printIRDataShort(Print *aSerial, IRData *aIRDataPtr) {

    aSerial->print(F("Protocol="));
    aSerial->print(getProtocolString(aIRDataPtr->protocol));
#if defined(DECODE_DISTANCE_WIDTH)
        if (aIRDataPtr->protocol != PULSE_DISTANCE && aIRDataPtr->protocol != PULSE_WIDTH) {
#endif
    /*
     * New decoders have address and command
     */
    aSerial->print(F(" Address=0x"));
    aSerial->print(aIRDataPtr->address, HEX);

    aSerial->print(F(" Command=0x"));
    aSerial->print(aIRDataPtr->command, HEX);

#if defined(DECODE_DISTANCE_WIDTH)
        }
#endif

    /*
     * Print number of bits
     */
    aSerial->print(' ');
    aSerial->print(aIRDataPtr->numberOfBits, DEC);
    aSerial->print(F(" bits"));

    if (aIRDataPtr->flags & IRDATA_FLAGS_IS_MSB_FIRST) {
        aSerial->print(F(" MSB first"));
    } else {
        aSerial->print(F(" LSB first"));
    }

    /*
     * Print gap and duration, in order to be able to compute the repeat period of the protocol by adding the next gap time
     */
    if (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
        aSerial->print(' ');
        if (aIRDataPtr->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) {
            aSerial->print(F("Auto-"));
        }
        aSerial->print(F("Repeat"));
    }
    aSerial->println();
}

/**
 * Deprecated static function to be able to print data to send or copied received data.
 * Ends with println().
 *
 * @param aSerial           The Print object on which to write, for Arduino you can use &Serial.
 * @param aIRDataPtr        Pointer to the data to be printed.
 * @param aPrintRepeatGap   If true also print the gap before repeats.
 *
 */
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintRepeatGap) {
    // DEPRECATED

    (void) aPrintRepeatGap;
    printIRDataShort(aSerial, aIRDataPtr);
}
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr) {
    // DEPRECATED

    if (aIRDataPtr->flags & IRDATA_FLAGS_WAS_OVERFLOW) {
        aSerial->println(F("Overflow"));
        return;
    }
    aSerial->print(F("Protocol="));
    aSerial->print(getProtocolString(aIRDataPtr->protocol));
    if (aIRDataPtr->protocol == UNKNOWN) {
#if defined(DECODE_HASH)
            aSerial->print(F(" Hash=0x"));
#if (__INT_WIDTH__ < 32)
            aSerial->print(aIRDataPtr->decodedRawData, HEX);
#else
            PrintULL::print(aSerial,aIRDataPtr->decodedRawData, HEX);
#endif

#endif
#if !defined(DISABLE_CODE_FOR_RECEIVER)
        aSerial->print(' ');
        aSerial->print((aIRDataPtr->rawlen + 1) / 2, DEC);
        aSerial->println(F(" bits (incl. gap and start) received"));
#endif
    } else {
#if defined(DECODE_DISTANCE_WIDTH)
            if (aIRDataPtr->protocol != PULSE_DISTANCE && aIRDataPtr->protocol != PULSE_WIDTH) {
#endif
        /*
         * New decoders have address and command
         */
        aSerial->print(F(" Address=0x"));
        aSerial->print(aIRDataPtr->address, HEX);

        aSerial->print(F(" Command=0x"));
        aSerial->print(aIRDataPtr->command, HEX);

        if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) {
            aSerial->print(F(" Extra=0x"));
            aSerial->print(aIRDataPtr->extra, HEX);
        }

        if (aIRDataPtr->flags & IRDATA_FLAGS_PARITY_FAILED) {
            aSerial->print(F(" Parity fail"));
        }

        if (aIRDataPtr->flags & IRDATA_FLAGS_TOGGLE_BIT) {
            aSerial->print(F(" Toggle=1"));
        }
#if defined(DECODE_DISTANCE_WIDTH)
            }
#endif

        /*
         * Print raw data
         */
        if (!(aIRDataPtr->flags & IRDATA_FLAGS_IS_REPEAT) || aIRDataPtr->decodedRawData != 0) {
            aSerial->print(F(" Raw-Data=0x"));
#if (__INT_WIDTH__ < 32)
            aSerial->print(aIRDataPtr->decodedRawData, HEX);
#else
            PrintULL::print(aSerial, aIRDataPtr->decodedRawData, HEX);
#endif
            /*
             * Print number of bits processed
             */
            aSerial->print(' ');
            aSerial->print(aIRDataPtr->numberOfBits, DEC);
            aSerial->print(F(" bits"));

            if (aIRDataPtr->flags & IRDATA_FLAGS_IS_MSB_FIRST) {
                aSerial->print(F(" MSB first"));
            } else {
                aSerial->print(F(" LSB first"));
            }
        }

        /*
         * Print gap and duration, in order to be able to compute the repeat period of the protocol by adding the next gap time
         */
        if (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
            aSerial->print(' ');
            if (aIRDataPtr->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) {
                aSerial->print(F("Auto-"));
            }
            aSerial->print(F("Repeat"));
        }
#if !defined(DISABLE_CODE_FOR_RECEIVER)
        aSerial->print(F(" Gap="));
        aSerial->print((uint32_t) aIRDataPtr->initialGapTicks * MICROS_PER_TICK);
        aSerial->println(F("us"));
#else
            aSerial->println();
#endif
    }
}

void IRrecv::printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo) {
    aSerial->print(aDistanceWidthTimingInfo->HeaderMarkMicros);
    aSerial->print(F(", "));
    aSerial->print(aDistanceWidthTimingInfo->HeaderSpaceMicros);
    aSerial->print(F(", "));
    aSerial->print(aDistanceWidthTimingInfo->OneMarkMicros);
    aSerial->print(F(", "));
    aSerial->print(aDistanceWidthTimingInfo->OneSpaceMicros);
    aSerial->print(F(", "));
    aSerial->print(aDistanceWidthTimingInfo->ZeroMarkMicros);
    aSerial->print(F(", "));
    aSerial->print(aDistanceWidthTimingInfo->ZeroSpaceMicros);
}

/*
 * Get maximum of mark ticks in rawDataPtr.
 * Skip leading start and trailing stop bit.
 */
uint8_t IRrecv::getMaximumMarkTicksFromRawData() {
    uint8_t tMaximumTick = 0;
    for (IRRawlenType i = 3; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit.
        auto tTick = irparams.rawbuf[i];
        if (tMaximumTick < tTick) {
            tMaximumTick = tTick;
        }
    }
    return tMaximumTick;
}
uint8_t IRrecv::getMaximumSpaceTicksFromRawData() {
    uint8_t tMaximumTick = 0;
    for (IRRawlenType i = 4; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit.
        auto tTick = irparams.rawbuf[i];
        if (tMaximumTick < tTick) {
            tMaximumTick = tTick;
        }
    }
    return tMaximumTick;
}

/*
 * The optimizing compiler internally generates this function, if getMaximumMarkTicksFromRawData() and getMaximumSpaceTicksFromRawData() is used.
 */
uint8_t IRrecv::getMaximumTicksFromRawData(bool aSearchSpaceInsteadOfMark) {
    uint8_t tMaximumTick = 0;
    IRRawlenType i;
    if (aSearchSpaceInsteadOfMark) {
        i = 4;
    } else {
        i = 3;
    }
    for (; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit.
        auto tTick = irparams.rawbuf[i];
        if (tMaximumTick < tTick) {
            tMaximumTick = tTick;
        }
    }
    return tMaximumTick;
}

uint32_t IRrecv::getTotalDurationOfRawData() {
    uint16_t tSumOfDurationTicks = 0;

    for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) {
        tSumOfDurationTicks += irparams.rawbuf[i];
    }
    return (uint32_t) tSumOfDurationTicks * MICROS_PER_TICK;
}

// @formatter:off

void printNumberOfRepeats(Print *aSerial) {
    aSerial->print(F(", <numberOfRepeats>"));
}

/**
 * Function to print values and flags of IrReceiver.decodedIRData in one line.
 * do not print for repeats except IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT.
 * Ends with println().
 * !!!Attention: The result differs on a 8 bit or 32 bit platform!!!
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRSendUsage(Print *aSerial) {
    if (((decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) == 0x00
                    || (decodedIRData.flags & IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT))) {

        /*
         * Generating the string:
         * Send on a 8 bit platform with:
         *     uint32_t tRawData[]={0x87654321, 0xAFEDCBA9, 0x5A};
         *     IrSender.send
         * or
         * Send on a 8 bit platform with: IrSender.send
         * or
         * Send with: IrSender.send
         */
#if defined(DECODE_DISTANCE_WIDTH)
        uint_fast8_t tNumberOfArrayData = 0;
        if (decodedIRData.protocol == PULSE_DISTANCE || decodedIRData.protocol == PULSE_WIDTH) {
#  if __INT_WIDTH__ < 32
            aSerial->print(F("Send on a 8 bit platform with: "));
            tNumberOfArrayData = ((decodedIRData.numberOfBits - 1) / 32) + 1;
            if(tNumberOfArrayData > 1) {
                aSerial->println();
                aSerial->print(F("  uint32_t tRawData[]={0x"));
                for (uint_fast8_t i = 0; i < tNumberOfArrayData; ++i) {
                    aSerial->print(decodedIRData.decodedRawDataArray[i], HEX);
#  else
            aSerial->print(F("Send on a 32 bit platform with: "));
            tNumberOfArrayData = ((decodedIRData.numberOfBits - 1) / 64) + 1;
            if(tNumberOfArrayData > 1) {
                aSerial->println();
                aSerial->print(F("  uint64_t tRawData[]={0x"));
                for (uint_fast8_t i = 0; i < tNumberOfArrayData; ++i) {
                    PrintULL::print(aSerial, decodedIRData.decodedRawDataArray[i], HEX);
#  endif
                    if (i != tNumberOfArrayData - 1) {
                        aSerial->print(F(", 0x"));
                    }
                }
                aSerial->println(F("};"));
                aSerial->print(F("  "));
            }
        } else { // if (decodedIRData.protocol == PULSE_DISTANCE || decodedIRData.protocol == PULSE_WIDTH)
            aSerial->print(F("Send with: "));
        }
        if (decodedIRData.protocol == UNKNOWN){
            aSerial->println();
            aSerial->print(F("  "));
            printIRResultAsCArray(aSerial);
            aSerial->print(F("  "));
        }
        aSerial->print(F("IrSender.send"));

#else
        aSerial->print(F("Send with: IrSender.send"));
#endif // #if defined(DECODE_DISTANCE_WIDTH)

        /*
         * Generating the string:
         * Raw(rawIRTimings, sizeof(rawIRTimings) / sizeof(rawIRTimings[0]), 38, <RepeatPeriodMillis>, <numberOfRepeats>
         * or
         * PulseDistanceWidth(38, 550, 1250, 500, 1250, 1300, 400, 0x3D9EAC, 23, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
         * or
         * PulseDistanceWidthFromArray(38, 8900, 4350, 600, 1650, 600, 550, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
         * or
         * <Protocol_Name>(0x<Address>, 0x<Command>, <numberOfRepeats>);
         * or
         * FAST(0x<Command>, <numberOfRepeats>);
         */
        if (decodedIRData.protocol == UNKNOWN){
            aSerial->print(F("Raw(rawIRTimings, sizeof(rawIRTimings) / sizeof(rawIRTimings[0]), 38, <RepeatPeriodMillis>"));
            printNumberOfRepeats(aSerial);
        }
#if defined(DECODE_DISTANCE_WIDTH)
        else if (decodedIRData.protocol == PULSE_DISTANCE || decodedIRData.protocol == PULSE_WIDTH) {
            /*
             * Pulse distance or pulse width here
             */
            aSerial->print(F("PulseDistanceWidth"));
            if(tNumberOfArrayData > 1) {
                aSerial->print(F("FromArray(38, "));
            } else {
                aSerial->print(F("(38, "));
            }
            printDistanceWidthTimingInfo(aSerial, &decodedIRData.DistanceWidthTimingInfo);

            if(tNumberOfArrayData > 1) {
                aSerial->print(F(", &tRawData[0], "));
            } else {
                aSerial->print(F(", 0x"));
#  if (__INT_WIDTH__ < 32)
                aSerial->print(decodedIRData.decodedRawData, HEX);
#  else
                PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
#  endif
                aSerial->print(F(", "));
            }
            aSerial->print(decodedIRData.numberOfBits); // aNumberOfBits
            aSerial->print(F(", PROTOCOL_IS_"));

            if (decodedIRData.flags & IRDATA_FLAGS_IS_MSB_FIRST) {
                aSerial->print('M');
            } else {
                aSerial->print('L');
            }
            aSerial->print(F("SB_FIRST, <RepeatPeriodMillis>"));
            printNumberOfRepeats(aSerial);

        }
#endif // defined(DECODE_DISTANCE_WIDTH)
        else {
            /*
             * Regular protocols here
             */
            aSerial->print(getProtocolString());
            aSerial->print(F("(0x"));
#if defined(DECODE_MAGIQUEST)
            if (decodedIRData.protocol == MAGIQUEST) {
#  if (__INT_WIDTH__ < 32)
                aSerial->print(decodedIRData.decodedRawData, HEX);
                aSerial->print(F(", 0x"));
#  else
                PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
                aSerial->print(F(", 0x"));
#  endif
            } else {
#  if defined(DECODE_FAST)
                // do not print address parameter for FAST protocol
                if (decodedIRData.protocol != FAST) {
                    aSerial->print(decodedIRData.address, HEX);
                    aSerial->print(F(", 0x"));
                }
#  else
                aSerial->print(decodedIRData.address, HEX);
                aSerial->print(F(", 0x"));
#  endif
            }
#else
            aSerial->print(decodedIRData.address, HEX);
            aSerial->print(F(", 0x"));
#endif

            aSerial->print(decodedIRData.command, HEX);
            if (decodedIRData.protocol == SONY) {
                printNumberOfRepeats(aSerial);
                aSerial->print(F(", "));
                aSerial->print(decodedIRData.numberOfBits);
            } else {
                printNumberOfRepeats(aSerial);

            }
        }

#if defined(DECODE_KASEIKYO) || defined(DECODE_RC6) || defined(DECODE_MARANTZ)
        if ((decodedIRData.flags & IRDATA_FLAGS_EXTRA_INFO) && (decodedIRData.protocol == KASEIKYO || decodedIRData.protocol == RC6A || decodedIRData.protocol == MARANTZ)) {
            // Vendor code, Customer or MarantzExtension parameter, which is after numberOfRepeats parameter
            aSerial->print(F(", 0x"));
            aSerial->print(decodedIRData.extra, HEX);
        }
#endif
        aSerial->print(F(");"));
        aSerial->println();
    }
}

// @formatter:on
/**
 * Function to print protocol number, address, command, raw data and repeat flag of IrReceiver.decodedIRData in one short line.
 * Does not print a Newline / does not end with println().
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRResultMinimal(Print *aSerial) {
    aSerial->print(F("P="));
    aSerial->print(decodedIRData.protocol);
    if (decodedIRData.protocol == UNKNOWN) {
#if defined(DECODE_HASH)
            aSerial->print(F(" #=0x"));
#  if (__INT_WIDTH__ < 32)
            aSerial->print(decodedIRData.decodedRawData, HEX);
#  else
            PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
#  endif
#endif
        aSerial->print(' ');
        aSerial->print((decodedIRData.rawlen + 1) / 2);
        aSerial->println(F(" bits received"));
    } else {
        /*
         * New decoders have address and command
         */
        aSerial->print(F(" A=0x"));
        aSerial->print(decodedIRData.address, HEX);

        aSerial->print(F(" C=0x"));
        aSerial->print(decodedIRData.command, HEX);

        aSerial->print(F(" Raw=0x"));
#if (__INT_WIDTH__ < 32)
        aSerial->print(decodedIRData.decodedRawData, HEX);
#else
        PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
#endif

        if (decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
            aSerial->print(F(" R"));
        }
    }
}

/*
 * Not used yet
 */
void IRrecv::printIRDuration(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) {
    uint16_t tSumOfDurationTicks = 0;
    for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) {
        tSumOfDurationTicks += irparams.rawbuf[i];
    }
    aSerial->print(F("Duration="));
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK);
        aSerial->println(F("us"));

    } else {
        aSerial->print(tSumOfDurationTicks);
        aSerial->println(F(" ticks"));
    }
}

/**
 * Dump out the timings in IrReceiver.irparams.rawbuf[] array 8 values per line.
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability.
 */
void IRrecv::printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) {

// Print Raw data
    aSerial->print(F("rawIRTimings["));
    aSerial->print(decodedIRData.rawlen);
    aSerial->println(F("]: "));

    /*
     * Print initial gap
     */
    aSerial->print(F(" -"));
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->println((uint32_t) decodedIRData.initialGapTicks * MICROS_PER_TICK);
    } else {
        aSerial->println(decodedIRData.initialGapTicks);
    }

// Newline is printed every 8. value, if tCounterForNewline % 8 == 0
    uint_fast8_t tCounterForNewline = 6; // first newline is after the 2 values of the start bit

// check if we have a protocol with no or 8 start bits
#if defined(DECODE_DENON) || defined(DECODE_MAGIQUEST)
        if (
#  if defined(DECODE_DENON)
                decodedIRData.protocol == DENON || decodedIRData.protocol == SHARP ||
#  endif
#  if defined(DECODE_MAGIQUEST)
                decodedIRData.protocol == MAGIQUEST ||
#  endif
                false) {
            tCounterForNewline = 0; // no or 8 start bits
        }
#endif

    uint32_t tDuration;
    uint16_t tSumOfDurationTicks = 0;
    for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) {
        auto tCurrentTicks = irparams.rawbuf[i];
        if (aOutputMicrosecondsInsteadOfTicks) {
            tDuration = tCurrentTicks * MICROS_PER_TICK;
        } else {
            tDuration = tCurrentTicks;
        }
        tSumOfDurationTicks += tCurrentTicks; // compute length of protocol frame

        if (!(i & 1)) {  // even
            aSerial->print('-');
        } else {  // odd
            aSerial->print(F(" +"));
        }

        // padding only for big values
        if (aOutputMicrosecondsInsteadOfTicks && tDuration < 1000) {
            aSerial->print(' ');
        }
        if (aOutputMicrosecondsInsteadOfTicks && tDuration < 100) {
            aSerial->print(' ');
        }
        if (tDuration < 10) {
            aSerial->print(' ');
        }
        aSerial->print(tDuration);

        if ((i & 1) && (i + 1) < decodedIRData.rawlen) {
            aSerial->print(','); //',' not required for last one
        }

        tCounterForNewline++;
        if ((tCounterForNewline % 8) == 0) {
            aSerial->println();
        }
    }

    aSerial->println();
    aSerial->print(F("Duration="));
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK);
        aSerial->println(F("us"));

    } else {
        aSerial->print(tSumOfDurationTicks);
        aSerial->println(F(" ticks"));
    }
    aSerial->println();
}

/**
 * Dump out the IrReceiver.irparams.rawbuf[] to be used as C definition for sendRaw().
 *
 *
 * Print ticks in 8 bit format to save space.
 * Maximum is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence.
 * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms).
 *
 * @param aSerial   The Print object on which to write, for Arduino you can use &Serial.
 * @param aOutputMicrosecondsInsteadOfTicks     Output the (rawbuf_values * MICROS_PER_TICK) for better readability.
 * @param aDoCompensate     Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
 */
void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) {
    printIRResultAsCArray(aSerial, aOutputMicrosecondsInsteadOfTicks, true);
}
void IRrecv::printIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks, bool aDoCompensate) {
// Start declaration
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->print(F("uint16_t rawIRTimings["));         // variable type, array name
    } else {
        aSerial->print(F("uint8_t rawTicks["));          // variable type, array name
    }

    aSerial->print(decodedIRData.rawlen - 1);    // array size
    aSerial->print(F("] = {"));    // Start declaration

// Dump data
    for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) {
        uint32_t tDuration = irparams.rawbuf[i] * MICROS_PER_TICK; // no problem to use 50 instead of 50L here!

        if (aDoCompensate) {
            if (i & 1) {
                // Mark
                tDuration -= MARK_EXCESS_MICROS;
            } else {
                tDuration += MARK_EXCESS_MICROS;
            }
        }

        if (aOutputMicrosecondsInsteadOfTicks) {
            aSerial->print(tDuration);
        } else {
            unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK;
            /*
             * Clip to 8 bit value
             */
            tTicks = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks;
            aSerial->print(tTicks);
        }
        if (i + 1 < decodedIRData.rawlen) aSerial->print(',');                // ',' not required on last one
        if (!(i & 1)) aSerial->print(' ');
    }

// End declaration
    aSerial->print(F("};"));                //

// Comment
    aSerial->print(F("  // "));
    printIRResultShort(aSerial);
}

/**
 * Store the decodedIRData to be used for sendRaw().
 *
 * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding and store it in an array.
 *
 * Maximum for uint8_t is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR frame.
 * @param aArrayPtr Address of an array provided by the caller.
 */
void IRrecv::compensateAndStoreIRResultInArray(uint8_t *aArrayPtr) {

// Store data, skip leading space#
    IRRawlenType i;
    for (i = 1; i < decodedIRData.rawlen; i++) {
        uint32_t tDuration = irparams.rawbuf[i] * MICROS_PER_TICK; // no problem to use 50 instead of 50L here!
        if (i & 1) {
            // Mark
            tDuration -= MARK_EXCESS_MICROS;
        } else {
            tDuration += MARK_EXCESS_MICROS;
        }

        unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK;
        *aArrayPtr = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // we store it in an 8 bit array
        aArrayPtr++;
    }
}

/**
 * Print results as C variables to be used for sendXXX()
 * uint16_t address = 0x44;
 * uint16_t command = 0x11;
 * uint32_t rawData = 0x44BB11EE;
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRResultAsCVariables(Print *aSerial) {
// Now dump "known" codes
    if (decodedIRData.protocol != UNKNOWN) {

        /*
         * New decoders have address and command
         */
        aSerial->print(F("uint16_t"));
        aSerial->print(F(" address = 0x"));
        aSerial->print(decodedIRData.address, HEX);
        aSerial->println(';');

        aSerial->print(F("uint16_t"));
        aSerial->print(F(" command = 0x"));
        aSerial->print(decodedIRData.command, HEX);
        aSerial->println(';');

        // All protocols have raw data
#if __INT_WIDTH__ < 32
        aSerial->print(F("uint32_t rawIRTimings = 0x"));
#else
        aSerial->print(F("uint64_t rawIRTimings = 0x"));
#endif
#if (__INT_WIDTH__ < 32)
        aSerial->print(decodedIRData.decodedRawData, HEX);
#else
        PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX);
#endif
        aSerial->println(';');
    }
}

#if defined(__AVR__)
const __FlashStringHelper* IRrecv::getProtocolString() {
// call no class function with same name
    return ::getProtocolString(decodedIRData.protocol);
}
#else
const char* IRrecv::getProtocolString() {
    // call no class function with same name
    return ::getProtocolString(decodedIRData.protocol);
}
#endif

/**********************************************************************************************************************
 * The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials
 * This function calls the old MSB first decoders and fills only the 3 variables:
 * aResults->value
 * aResults->bits
 * aResults->decode_type
 **********************************************************************************************************************/
bool IRrecv::decode_old(decode_results *aResults) {

    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return false;
    }

// copy for usage by legacy programs
    aResults->rawbuf[0] = irparams.initialGapTicks;
    for (int i = 1; i < RAW_BUFFER_LENGTH; ++i) {
        aResults->rawbuf[i] = irparams.rawbuf[i]; // copy 8 bit array into a 16 bit array
    }
    aResults->rawlen = irparams.rawlen;
    if (irparams.OverflowFlag) {
        // Copy overflow flag to decodedIRData.flags
        irparams.OverflowFlag = false;
        irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call
        DEBUG_PRINTLN(F("Overflow happened"));
    }
    aResults->overflow = irparams.OverflowFlag;
    aResults->value = 0;

    decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; // for print

#if defined(DECODE_NEC)
        DEBUG_PRINTLN(F("Attempting old NEC decode"));
        if (decodeNECMSB(aResults)) {
            return true;
        }
#endif

#if defined(DECODE_SONY)
        DEBUG_PRINTLN(F("Attempting old Sony decode"));
        if (decodeSonyMSB(aResults)) {
            return true;
        }
#endif

#if defined(DECODE_RC5)
        DEBUG_PRINTLN(F("Attempting RC5 decode"));
        if (decodeRC5()) {
            aResults->bits = decodedIRData.numberOfBits;
            aResults->value = decodedIRData.decodedRawData;
            aResults->decode_type = RC5;

            return true;
        }
#endif

#if defined(DECODE_RC6)
        DEBUG_PRINTLN(F("Attempting RC6 decode"));
        if (decodeRC6()) {
            aResults->bits = decodedIRData.numberOfBits;
            aResults->value = decodedIRData.decodedRawData;
            aResults->decode_type = RC6;
            return true;
        }
#endif

//    Removed bool IRrecv::decodePanasonicMSB(decode_results *aResults) since  implementations was wrong (wrong length), and nobody recognized it

#if defined(DECODE_LG)
        DEBUG_PRINTLN(F("Attempting old LG decode"));
        if (decodeLGMSB(aResults)) {return true;}
#endif

#if defined(DECODE_JVC)
        DEBUG_PRINTLN(F("Attempting old JVC decode"));
        if (decodeJVCMSB(aResults)) {
            return true;
        }
#endif

#if defined(DECODE_SAMSUNG)
        DEBUG_PRINTLN(F("Attempting old SAMSUNG decode"));
        if (decodeSAMSUNG(aResults)) {
            return true;
        }
#endif

#if defined(DECODE_DENON)
        DEBUG_PRINTLN(F("Attempting old Denon decode"));
        if (decodeDenonOld(aResults)) {
            return true;
        }
#endif

// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
// If you add any decodes, add them before this.
    if (decodeHashOld(aResults)) {
        return true;
    }
// Throw away and start over
    resume();
    return false;
}

/** @}*/
#if defined(_IR_MEASURE_TIMING)
#undef _IR_MEASURE_TIMING
#endif
#include "LocalDebugLevelEnd.h"

#endif // _IR_RECEIVE_HPP
