/*
 * ir_Kaseikyo.hpp
 *
 *  Contains functions for receiving and sending Kaseikyo/Panasonic IR Protocol in "raw" and standard format with 16 bit address + 8 bit command
 *
 *  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_KASEIKYO_HPP
#define _IR_KASEIKYO_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
 * @{
 */
//==============================================================================
//       K  K   AA    SSS   EEEE  III  K  K  Y   Y   OOO
//       K K   A  A  S      E      I   K K    Y Y   O   O
//       KK    AAAA   SSS   EEE    I   KK      Y    O   O
//       K K   A  A      S  E      I   K K     Y    O   O
//       K  K  A  A  SSSS   EEEE  III  K  K    Y     OOO
//==============================================================================
//==============================================================================
//       PPPP    AAA   N   N   AAA    SSSS   OOO   N   N  IIIII   CCCC
//       P   P  A   A  NN  N  A   A  S      O   O  NN  N    I    C
//       PPPP   AAAAA  N N N  AAAAA   SSS   O   O  N N N    I    C
//       P      A   A  N  NN  A   A      S  O   O  N  NN    I    C
//       P      A   A  N   N  A   A  SSSS    OOO   N   N  IIIII   CCCC
//==============================================================================
/*
 Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first
 +3450,-1700
 + 450,- 400 + 500,-1250 + 450,- 400 + 500,- 400
 + 450,- 400 + 400,- 450 + 500,- 350 + 450,- 450
 + 450,- 400 + 450,- 400 + 500,- 400 + 450,- 400
 + 450,- 400 + 500,-1250 + 450,- 400 + 500,- 350
 + 500,- 400 + 450,- 400 + 450,- 450 + 450,- 400
 + 450,-1250 + 500,- 400 + 450,- 400 + 450,- 400
 + 450,-1300 + 450,-1250 + 450,-1300 + 400,-1300
 + 450,-1300 + 450,-1250 + 450,-1250 + 500,-1250
 + 450,- 450 + 450,-1250 + 450,-1250 + 500,- 400
 + 450,-1250 + 450,-1300 + 450,-1250 + 450,- 450
 + 450,-1250 + 450,- 400 + 450,- 400 + 500,-1250
 + 450,-1250 + 450,- 400 + 500,- 400 + 450,-1250
 + 450
 Sum: 64300
 */
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Panasonic
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo
// LSB first
// The first two (8-bit) bytes contains the vendor code.
// The next 4 bit is VendorID parity.
// The last byte is parity (XOR) of the 3 bytes before.
// There are multiple interpretations of the next fields:
// IRP: {37k,432}<1,-1|1,-3>(8,-4,M:8,N:8,X:4,D:4,S:8,F:8,G:8,1,-173)+ {X=M:4:0^M:4:4^N:4:0^N:4:4}
// 1. interpretation: 4 bit Device, 8 bitSubdevice and 8 bit function.
//    0_______ 1_______  2______  3_______ 4_______ 5_______
//    01234567 89ABCDEF  01234567 01234567 01234567 01234567
//    01000000 00100100  0110Dev_ Sub_Dev_ Fun____  XOR( B2, B3, B4) - Byte 0,1 and vendor parity showing Panasonic vendor code 0x2002.
// 1. interpretation: <start bit><VendorID:16><VendorID parity:4><Device:4><Subdevice:8><Function:8><Parity:8><stop bit>
// see: http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152
// 2. interpretation (Flipper Zero style): <start bit><VendorID:16><VendorID parity:4><Genre1:4><Genre2:4><Command:10><ID:2><Parity:8><stop bit>
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#KASEIKYO
// Implemented is Samsung style:  <start bit><VendorID:16><VendorID parity:4><Address:12><Command:8><Parity of VendorID parity, Address and Command:8><stop bit>
//                  which is derived from Samsung remotes and may not be optimal for Denon kind of Kaseikyo protokol usage.
//
#define KASEIKYO_VENDOR_ID_BITS     16
#define KASEIKYO_VENDOR_ID_PARITY_BITS   4
#define KASEIKYO_ADDRESS_BITS       12
#define KASEIKYO_COMMAND_BITS       8
#define KASEIKYO_PARITY_BITS        8
#define KASEIKYO_BITS               (KASEIKYO_VENDOR_ID_BITS + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS) // 48
#define KASEIKYO_UNIT               432 // 16 pulses of 37 kHz (432,432432)  - Pronto 0x70 | 0x10

#define KASEIKYO_HEADER_MARK        (8 * KASEIKYO_UNIT) // 3456
#define KASEIKYO_HEADER_SPACE       (4 * KASEIKYO_UNIT) // 1728

#define KASEIKYO_BIT_MARK           KASEIKYO_UNIT
#define KASEIKYO_ONE_SPACE          (3 * KASEIKYO_UNIT) // 1296
#define KASEIKYO_ZERO_SPACE         KASEIKYO_UNIT

#define KASEIKYO_AVERAGE_DURATION   56000
#define KASEIKYO_REPEAT_PERIOD      130000
#define KASEIKYO_REPEAT_DISTANCE    (KASEIKYO_REPEAT_PERIOD - KASEIKYO_AVERAGE_DURATION) // 74 ms
#define KASEIKYO_MAXIMUM_REPEAT_DISTANCE    (KASEIKYO_REPEAT_DISTANCE + (KASEIKYO_REPEAT_DISTANCE / 4)) // Just a guess

#define PANASONIC_VENDOR_ID_CODE    0x2002
#define DENON_VENDOR_ID_CODE        0x3254
#define MITSUBISHI_VENDOR_ID_CODE   0xCB23
#define SHARP_VENDOR_ID_CODE        0x5AAA
#define JVC_VENDOR_ID_CODE          0x0103

struct PulseDistanceWidthProtocolConstants const KaseikyoProtocolConstants PROGMEM = {KASEIKYO, KASEIKYO_KHZ, KASEIKYO_HEADER_MARK,
    KASEIKYO_HEADER_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST | PROTOCOL_IS_PULSE_DISTANCE
    , (KASEIKYO_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr};

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

/**
 * Address can be interpreted as sub-device << 4 + 4 bit device
 */
void IRsend::sendKaseikyo(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode) {
    // Set IR carrier frequency
    enableIROut (KASEIKYO_KHZ); // 37 kHz

    // Vendor Parity
    uint8_t tVendorParity = aVendorCode ^ (aVendorCode >> 8);
    tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;

#if __INT_WIDTH__ < 32
    LongUnion tSendValue;
    // Compute parity
    tSendValue.UWord.LowWord = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble with vendor parity
    tSendValue.UBytes[2] = aCommand;
    tSendValue.UBytes[3] = aCommand ^ tSendValue.UBytes[0] ^ tSendValue.UBytes[1]; // 8 bit parity of 3 bytes command, address and vendor parity
    IRRawDataType tRawKaseikyoData[2];
    tRawKaseikyoData[0] = (uint32_t) tSendValue.UWord.LowWord << 16 | aVendorCode; // LSB of tRawKaseikyoData[0] is sent first
    tRawKaseikyoData[1] = tSendValue.UWord.HighWord;
    sendPulseDistanceWidthFromArray_P(&KaseikyoProtocolConstants, &tRawKaseikyoData[0], KASEIKYO_BITS, aNumberOfRepeats);
#else
    LongLongUnion tSendValue;
    tSendValue.UWords[0] = aVendorCode;
    // Compute parity
    tSendValue.UWords[1] = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble to parity
    tSendValue.UBytes[4] = aCommand;
    tSendValue.UBytes[5] = aCommand ^ tSendValue.UBytes[2] ^ tSendValue.UBytes[3]; // Parity
    sendPulseDistanceWidth_P(&KaseikyoProtocolConstants, tSendValue.ULongLong, KASEIKYO_BITS, aNumberOfRepeats);
#endif
}

/**
 * Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE
 */
void IRsend::sendPanasonic(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, PANASONIC_VENDOR_ID_CODE);
}

/**
 * Stub using Kaseikyo with DENON_VENDOR_ID_CODE
 */
void IRsend::sendKaseikyo_Denon(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, DENON_VENDOR_ID_CODE);
}

/**
 * Stub using Kaseikyo with MITSUBISHI_VENDOR_ID_CODE
 */
void IRsend::sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, MITSUBISHI_VENDOR_ID_CODE);
}

/**
 * Stub using Kaseikyo with SHARP_VENDOR_ID_CODE
 */
void IRsend::sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, SHARP_VENDOR_ID_CODE);
}

/**
 * Stub using Kaseikyo with JVC_VENDOR_ID_CODE
 */
void IRsend::sendKaseikyo_JVC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
    sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, JVC_VENDOR_ID_CODE);
}

/*
 * Tested with my Panasonic DVD/TV remote
 */
bool IRrecv::decodeKaseikyo() {

    decode_type_t tProtocol;
    // Check we have enough data (96 + 4) 4 for initial gap, start bit mark and space + stop bit mark
    if (decodedIRData.rawlen != ((2 * KASEIKYO_BITS) + 4)) {
        IR_DEBUG_PRINT(F("Kaseikyo: "));
        IR_DEBUG_PRINT(F("Data length="));
        IR_DEBUG_PRINT(decodedIRData.rawlen);
        IR_DEBUG_PRINTLN(F(" is not 100"));
        return false;
    }

    if (!checkHeader_P(&KaseikyoProtocolConstants)) {
        return false;
    }

    // decode first 16 Vendor ID bits
    decodePulseDistanceWidthData_P(&KaseikyoProtocolConstants, KASEIKYO_VENDOR_ID_BITS);

    uint16_t tVendorId = decodedIRData.decodedRawData;
    if (tVendorId == PANASONIC_VENDOR_ID_CODE) {
        tProtocol = PANASONIC;
    } else if (tVendorId == SHARP_VENDOR_ID_CODE) {
        tProtocol = KASEIKYO_SHARP;
    } else if (tVendorId == DENON_VENDOR_ID_CODE) {
        tProtocol = KASEIKYO_DENON;
    } else if (tVendorId == JVC_VENDOR_ID_CODE) {
        tProtocol = KASEIKYO_JVC;
    } else if (tVendorId == MITSUBISHI_VENDOR_ID_CODE) {
        tProtocol = KASEIKYO_MITSUBISHI;
    } else {
        tProtocol = KASEIKYO;
    }

    // Vendor Parity
    uint8_t tVendorParity = tVendorId ^ (tVendorId >> 8);
    tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;

    /*
     * Decode next 32 bits, 8 VendorID parity parity + 12 address (device and subdevice) + 8 command + 8 parity
     */
    decodePulseDistanceWidthData_P(&KaseikyoProtocolConstants,
    KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS,
            3 + (2 * KASEIKYO_VENDOR_ID_BITS));

    // Success
//    decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
    LongUnion tValue;
    tValue.ULong = decodedIRData.decodedRawData;
#if __INT_WIDTH__ >= 32
    // workaround until complete refactoring for 64 bit
    decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 16) | tVendorId; // store all 48 bits in decodedRawData
#endif
    decodedIRData.address = (tValue.UWord.LowWord >> KASEIKYO_VENDOR_ID_PARITY_BITS); // remove 4 bit vendor parity
    decodedIRData.command = tValue.UByte.MidHighByte;
    uint8_t tParity = tValue.UByte.LowByte ^ tValue.UByte.MidLowByte ^ tValue.UByte.MidHighByte;

    if (tVendorParity != (tValue.UByte.LowByte & 0xF)) {
        decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;

#if defined(LOCAL_DEBUG)
        Serial.print(F("Kaseikyo: "));
        Serial.print(F("4 bit VendorID parity is not correct. Expected=0x"));
        Serial.print(tVendorParity, HEX);
        Serial.print(F(" received=0x"));
        Serial.print(decodedIRData.decodedRawData, HEX);
        Serial.print(F(" VendorID=0x"));
        Serial.println(tVendorId, HEX);
#endif
    }

    if (tProtocol == KASEIKYO) {
        decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
        decodedIRData.extra = tVendorId; // Store (unknown) vendor ID
    }

    if (tValue.UByte.HighByte != tParity) {
        decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;

#if defined(LOCAL_DEBUG)
        Serial.print(F("Kaseikyo: "));
        Serial.print(F("8 bit parity is not correct. Expected=0x"));
        Serial.print(tParity, HEX);
        Serial.print(F(" received=0x"));
        Serial.print(decodedIRData.decodedRawData >> KASEIKYO_COMMAND_BITS, HEX);
        Serial.print(F(" address=0x"));
        Serial.print(decodedIRData.address, HEX);
        Serial.print(F(" command=0x"));
        Serial.println(decodedIRData.command, HEX);
#endif
    }

    decodedIRData.numberOfBits = KASEIKYO_BITS;
    decodedIRData.protocol = tProtocol;

    // check for repeat
    checkForRepeatSpaceTicksAndSetFlag(KASEIKYO_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);

    return true;
}

/*
 * Removed void IRsend::sendPanasonic(uint16_t aAddress, uint32_t aData)
 * and bool IRrecv::decodePanasonicMSB(decode_results *aResults)
 * since their implementations were wrong (wrong length), and nobody recognized it
 */

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