/** @file
    RFXMeter / RFXPower decoder.

    Copyright (C) 2022 Christian W. Zuckschwerdt <zany@triq.net>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
*/

#include "decoder.h"

/**
RFXMeter / RFXPower decoder.

S.a. https://github.com/merbanan/rtl_433/issues/2141

RFXMeter uses an X10RF-like frame with some variations.
The data has the same meaning and order as through RFXCom (RFXtrx).

The device uses PPM encoding,
- 0 is encoded as 0.5 ms pulse and 0.5 ms gap,
- 1 is encoded as 0.5 ms pulse and 1.5 ms gap.

A packet starts with a 9 ms pulse followed by a 4 ms gap.
Packet is 165 ms long, message is 139 ms long.
Packet is repeated 4 times for each address, with 38 ms between each packet.

A message is 48 bit / 6 bytes long:

    |     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |     Byte 4    |     Byte 5    |
     7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
     4               4                   3                   2                   1
     7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
     < - - - - - - address - - - - > < - - - - - - - - counter value - - - - - - - > < - - > <checksum>
                                     1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 2 2 2 2 1 1 1 1    |
                                     5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 3 2 1 0 9 8 7 6    packettype

2 bytes address. Byte 1 is byte 0 with the nibbles complemented, allowing a
maximum of 256 modules to be used.
The counter value is `256 * (256 * msb + isb) + lsb`.
The RFXPwr module counter is capable of measuring with an accuracy of 0.001 kWh
and can count from 0 to 16777.215 kWh.

Type of packets:

- 0000 normal data packet
- 0001 setting a new time interval.
   Byte 2
            0x01 30 seconds 0x02 1 minute
            0x04 6 minutes (RFXPower = 5 minutes)
            0x08 12 minutes (RFXPower = 10 minutes) 0x10 15 minutes
            0x20 30 minutes
            0x40 45 minutes
            0x80 60 minutes
- 0010 calibration value in `<counter value>` in usec.
- 0011 setting a new address
- 0100 counter value reset to 0 1011 enter counter value
- 1100 enter interval setting mode within 5 seconds
- 1101 enter calibration mode within 5 seconds
- 1110 enter address setting mode within 5 seconds
- 1111 identification packet
      Byte 2 = firmware version
              0x00 - 0x3F = RFXPower
              0x40 - 0x7F = RFU
              0x80 - 0xBF = RFU
              0xC0 - 0xFF = RFXMeter
      Byte 3 = time interval (see packet type 0001)

The 4 bit checksum is a nibble sum over the whole message, complemented to 0xf.

-X 'n=RFXMeter,m=OOK_PPM,s=500,l=1500,g=2500,r=5000'
*/

static int rfxmeter_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
    // All RfxMeter packets have one row, but a sync might be decoded as a leading short row.
    if (bitbuffer->num_rows != 1 && bitbuffer->num_rows != 2) {
        decoder_logf(decoder, 2, __func__, "Too many rows (%u)", bitbuffer->num_rows);
        return DECODE_ABORT_LENGTH;
    }

    // All RfxMeter packets have 48 bits.
    uint8_t *b = bitbuffer->bb[bitbuffer->num_rows - 1];
    if (bitbuffer->bits_per_row[bitbuffer->num_rows - 1] != 48) {
        decoder_logf(decoder, 2, __func__, "Expected 48 bits, got %u bits", bitbuffer->bits_per_row[bitbuffer->num_rows - 1]);
        return DECODE_ABORT_LENGTH;
    }

    // Check address complement
    if ((b[0] ^ 0xf0) != b[1]) {
        decoder_logf_bitrow(decoder, 1, __func__, b, 48, "Address error");
        return DECODE_FAIL_SANITY;
    }

    // Check message checksum, a nibble sum over the whole message complemented to 0xf
    int chk = add_nibbles(b, 6);
    if ((chk & 0x0f) != 0x0f) {
        decoder_logf_bitrow(decoder, 1, __func__, b, 48, "Checksum error");
        return DECODE_FAIL_MIC;
    }

    int address   = b[0];
    int msg_value = (b[4] << 16) | (b[2] << 8) | (b[3]);
    int msg_type  = b[5] >> 4;

    /* clang-format off */
    data_t *data = data_make(
            "model",        "",             DATA_STRING, "RfxMeter",
            "id",           "Id",           DATA_INT,    address,
            "msg_type",     "Msg Type",     DATA_INT,    msg_type,
            "msg_value",    "Msg Value",    DATA_INT,    msg_value,
            "mic",          "Integrity",    DATA_STRING, "CHECKSUM",
            NULL);
    /* clang-format on */

    decoder_output_data(decoder, data);
    return 1;
}

static char const *const output_fields[] = {
        "model",
        "id",
        "msg_type",
        "msg_value",
        "mic",
        NULL,
};

r_device const rfxmeter = {
        .name        = "RfxMeter, RFXPwr",
        .modulation  = OOK_PULSE_PPM,
        .short_width = 500,
        .long_width  = 1500,
        .gap_limit   = 2500,
        .reset_limit = 5000,
        .decode_fn   = &rfxmeter_decode,
        .fields      = output_fields,
};
