/*
 * ir_Denon.cpp
 *
 *  Contains functions for receiving and sending Denon/Sharp IR Protocol
 *
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 ************************************************************************************
 * MIT License
 *
 * Copyright (c) 2020-2023 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_DENON_HPP
#define _IR_DENON_HPP

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

/** \addtogroup Decoder Decoders and encoders for different protocols
 * @{
 */
//==============================================================================
//                    DDDD   EEEEE  N   N   OOO   N   N
//                     D  D  E      NN  N  O   O  NN  N
//                     D  D  EEE    N N N  O   O  N N N
//                     D  D  E      N  NN  O   O  N  NN
//                    DDDD   EEEEE  N   N   OOO   N   N
//==============================================================================
//                       SSSS  H   H   AAA   RRRR   PPPP
//                      S      H   H  A   A  R   R  P   P
//                       SSS   HHHHH  AAAAA  RRRR   PPPP
//                          S  H   H  A   A  R  R   P
//                      SSSS   H   H  A   A  R   R  P
//==============================================================================
/*
 Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first
 + 200,-1800 + 300,- 750 + 300,- 800 + 200,- 800
 + 250,-1800 + 250,- 800 + 250,-1800 + 300,-1750
 + 300,- 750 + 300,-1800 + 250,-1800 + 250,-1850
 + 250,- 750 + 300,- 800 + 250,- 800 + 250
 Sum: 23050

 Denon/Sharp variant
 Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0x4ED1 15 bits LSB first
 + 200,-1800 + 300,- 750 + 250,- 800 + 250,- 750
 + 300,-1800 + 250,- 800 + 250,-1800 + 300,-1750
 + 300,- 750 + 300,-1800 + 250,-1800 + 250,-1800
 + 300,- 750 + 300,- 750 + 300,-1800 + 250
 Sum: 23050
 */
/*
 * https://www.mikrocontroller.net/articles/IRMP_-_english#DENON
 * Denon published all their IR codes:
 * http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
 * Example:
 * 0000 006D 0000 0020 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
 *                     000A 001E 000A 001E 000A 0046 000A 0046 000A 0046 000A 001E 000A 0046 000A 0046 // 8 command bits
 *                     000A 001E 000A 001E 000A 0679 // 2 frame bits 0,0 + stop bit + space for AutoRepeat
 *                     000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
 *                     000A 0046 000A 0046 000A 001E 000A 001E 000A 001E 000A 0046 000A 001E 000A 001E // 8 inverted command bits
 *                     000A 0046 000A 0046 000A 0679 // 2 frame bits 1,1 + stop bit + space for Repeat
 * From analyzing the codes for Tuner preset 1 to 8 in tab Main Zone ID#1 it is obvious, that the protocol is LSB first at least for command.
 * All Denon codes with 32 as 3. value use the Kaseikyo Denon variant.
 */
// LSB first, no start bit, 5 address + 8 command + 2 frame (0,0) + 1 stop bit - each frame 2 times
// Every frame is auto repeated with a space period of 45 ms and the command and frame inverted to (1,1) or (0,1) for SHARP.
//
#define DENON_ADDRESS_BITS      5
#define DENON_COMMAND_BITS      8
#define DENON_FRAME_BITS        2 // 00/10 for 1. frame Denon/Sharp, inverted for autorepeat frame

#define DENON_BITS              (DENON_ADDRESS_BITS + DENON_COMMAND_BITS + DENON_FRAME_BITS) // 15 - The number of bits in the command
#define DENON_UNIT              260

#define DENON_BIT_MARK          DENON_UNIT  // The length of a Bit:Mark
#define DENON_ONE_SPACE         (7 * DENON_UNIT) // 1820 // The length of a Bit:Space for 1's
#define DENON_ZERO_SPACE        (3 * DENON_UNIT) // 780 // The length of a Bit:Space for 0's

#define DENON_AUTO_REPEAT_DISTANCE  45000 // Every frame is auto repeated with a space period of 45 ms and the command and frame inverted.
#define DENON_REPEAT_PERIOD        110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.

// for old decoder
#define DENON_HEADER_MARK       DENON_UNIT // The length of the Header:Mark
#define DENON_HEADER_SPACE      (3 * DENON_UNIT) // 780 // The length of the Header:Space

struct PulseDistanceWidthProtocolConstants const DenonProtocolConstants PROGMEM = {DENON, DENON_KHZ, DENON_HEADER_MARK, DENON_HEADER_SPACE,
    DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST | PROTOCOL_IS_PULSE_DISTANCE,
    (DENON_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr};

/************************************
 * Start of send and decode functions
 ************************************/

void IRsend::sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendDenon(aAddress, aCommand, aNumberOfRepeats, 1);
    // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1272
}

void IRsend::sendSharp2(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendDenon(aAddress, aCommand, aNumberOfRepeats, 2);
}

/*
 * Denon frames are always sent 3 times. A non inverted (normal), an inverted frame, ending with a normal frame.
 * Repeats are done by just adding an inverted and a normal frame with no extra delay, so it is quite responsible :-)
 * If you specify a repeat of e.g. 3, then 3 + 6 frames are sent.
 * Measured at Denon RC 1081.
 */
void IRsend::sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t aSendSharpFrameMarker) {
    // Set IR carrier frequency
    enableIROut (DENON_KHZ); // 38 kHz

    // Add frame marker for sharp
    uint16_t tCommand = aCommand;
    // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1272
    tCommand |= aSendSharpFrameMarker << 8; // the 2 upper bits are 00 for Denon and 01 or 10 for Sharp

    uint16_t tData = aAddress | ((uint16_t) tCommand << DENON_ADDRESS_BITS);
    uint16_t tInvertedData = (tData ^ 0x7FE0); // Command and frame (upper 10 bits, bit 5 to 14) are inverted

    uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
    while (tNumberOfCommands > 0) {

        // Data
        sendPulseDistanceWidthData_P(&DenonProtocolConstants, tData, DENON_BITS);

        // Inverted autorepeat frame
        delay(DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
        sendPulseDistanceWidthData_P(&DenonProtocolConstants, tInvertedData, DENON_BITS);

        tNumberOfCommands--;
        // send repeated command with a fixed space gap
        delay( DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
    }
    /*
     * always end with a normal frame
     * skip last delay!
     */
    sendPulseDistanceWidthData_P(&DenonProtocolConstants, tData, DENON_BITS);

}

bool IRrecv::decodeSharp() {
    return decodeDenon();
}

bool IRrecv::decodeDenon() {

    // we have no start bit, so check for the exact amount of data bits
    // Check we have the right amount of data (32). The + 2 is for initial gap + stop bit mark
    if (decodedIRData.rawlen != (2 * DENON_BITS) + 2) {
        IR_DEBUG_PRINT(F("Denon: "));
        IR_DEBUG_PRINT(F("Data length="));
        IR_DEBUG_PRINT(decodedIRData.rawlen);
        IR_DEBUG_PRINTLN(F(" is not 32"));
        return false;
    }

    // Try to decode as Denon protocol
    decodePulseDistanceWidthData_P(&DenonProtocolConstants, DENON_BITS, 1);

    // Check for stop mark, this stop bit distinguishes it from decoding as Sony
    if (!matchMark(irparams.rawbuf[(2 * DENON_BITS) + 1], DENON_HEADER_MARK)) {
#if defined(LOCAL_DEBUG)
        Serial.print(F("Denon: "));
        Serial.println(F("Stop bit mark length is wrong"));
#endif
        return false;
    }

    // Success
    decodedIRData.address = decodedIRData.decodedRawData & 0x1F;
    decodedIRData.command = decodedIRData.decodedRawData >> DENON_ADDRESS_BITS;
    uint8_t tFrameBits = (decodedIRData.command >> 8) & 0x03;
    decodedIRData.command &= 0xFF;

    // Check for (auto) repeat
    if (decodedIRData.initialGapTicks < ((DENON_AUTO_REPEAT_DISTANCE + (DENON_AUTO_REPEAT_DISTANCE / 4)) / MICROS_PER_TICK)) {
        decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT;

        repeatCount++;
        /*
         * 1. repeat is inverted autorepeat -> IRDATA_FLAGS_IS_AUTO_REPEAT
         * 2. repeat is normal autorepeat (end) or 1. normal repeat -> IRDATA_FLAGS_IS_AUTO_REPEAT
         * 3. repeat is inverted autorepeat of the 1. repeat -> IRDATA_FLAGS_IS_REPEAT (not the correct frame, but this enables easy counting of repeats)
         * 4. repeat is normal autorepeat (end) or 2. normal repeat -> IRDATA_FLAGS_IS_AUTO_REPEAT
         * 5. repeat is inverted autorepeat of the 2. repeat -> IRDATA_FLAGS_IS_REPEAT (not the correct frame, but this enables easy counting of repeats)
         * 6. repeat is normal autorepeat (end) or 3. normal repeat -> IRDATA_FLAGS_IS_AUTO_REPEAT
         */

        if (repeatCount & 0x01) {
            /*
             * Here we are in an inverted auto repeated frame where the command and frame bits are inverted
             */
#if defined(LOCAL_DEBUG)
            Serial.print(F("Denon: Inverted frame"));
#endif
            if (repeatCount > 1) { // skip first auto repeat
                decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
            }
            // Check parity of consecutive received commands. There is no parity in one data set.
            if ((uint8_t) lastDecodedCommand != (uint8_t)(~decodedIRData.command)) {
                decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
#if defined(LOCAL_DEBUG)
                Serial.print(F("Denon: Parity check of inverted with non inverted frame failed. Last command="));
                Serial.print((uint8_t)lastDecodedCommand, HEX);
                Serial.print(F(" current="));
                Serial.println((uint8_t)~decodedIRData.command, HEX);
#endif
            }
            // always take non inverted command
            decodedIRData.command = lastDecodedCommand;

            // inverted command here, re-invert the frame bits for later protocol decoding
            tFrameBits = tFrameBits ^ 0x03;
        }
    } else {
        // first command here
        repeatCount = 0;
    }

    if (tFrameBits) {
        decodedIRData.protocol = SHARP;
    } else {
        decodedIRData.protocol = DENON;
    }

    decodedIRData.numberOfBits = DENON_BITS;

    return true;
}

/*********************************************************************************
 * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
 *********************************************************************************/
/*
 * Only for backwards compatibility
 */
void IRsend::sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats) {
    sendDenon(aRawData >> (DENON_COMMAND_BITS + DENON_FRAME_BITS), (aRawData >> DENON_FRAME_BITS) & 0xFF, aNumberOfRepeats);
}

/*
 * Old function with parameter data
 */
void IRsend::sendDenon(unsigned long data, int nbits) {
    // Set IR carrier frequency
    enableIROut (DENON_KHZ);
#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
//    Serial.println(
//            "The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats).");
#endif

    // Header
    mark(DENON_HEADER_MARK);
    space(DENON_HEADER_SPACE);

    // Data
    sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, data, nbits,
            PROTOCOL_IS_MSB_FIRST);
}

/*
 * Old function without parameter aNumberOfRepeats
 */
void IRsend::sendSharp(uint16_t aAddress, uint16_t aCommand) {
    sendDenon(aAddress, aCommand, 0, true);
}

bool IRrecv::decodeDenonOld(decode_results *aResults) {

    // Check we have the right amount of data
    if (decodedIRData.rawlen != 1 + 2 + (2 * DENON_BITS) + 1) {
        return false;
    }

    // Check initial Mark+Space match
    if (!matchMark(aResults->rawbuf[1], DENON_HEADER_MARK)) {
        return false;
    }

    if (!matchSpace(aResults->rawbuf[2], DENON_HEADER_SPACE)) {
        return false;
    }

    // Try to decode as Denon protocol.
    decodePulseDistanceWidthData(DENON_BITS, 3, DENON_BIT_MARK, DENON_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST);

    // Success
    aResults->value = decodedIRData.decodedRawData;
    aResults->bits = DENON_BITS;
    aResults->decode_type = DENON;
    decodedIRData.protocol = DENON;
    return true;
}

/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_DENON_HPP
