#ifndef __FPROTO_OREGON3_H__
#define __FPROTO_OREGON3_H__

#include "weatherbase.hpp"

#define OREGON3_PREAMBLE_BITS 28
#define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111
// 24 ones + 0101 (inverted A)
#define OREGON3_PREAMBLE 0b1111111111111111111111110101

// Fixed part contains:
// - Sensor type: 16 bits
// - Channel: 4 bits
// - ID (changes when batteries are changed): 8 bits
// - Battery status: 4 bits
#define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4)
#define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF)
#define OREGON3_CHECKSUM_BITS 8

// bit indicating the low battery
#define OREGON3_FLAG_BAT_LOW 0x4

/// Documentation for Oregon Scientific protocols can be found here:
/// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf
// Sensors ID
#define ID_THGR221 0xf824
typedef enum {
    Oregon3DecoderStepReset = 0,
    Oregon3DecoderStepFoundPreamble,
    Oregon3DecoderStepVarData,
} Oregon3DecoderStep;

class FProtoWeatherOregon3 : public FProtoWeatherBase {
   public:
    FProtoWeatherOregon3() {
        sensorType = FPW_OREGON3;
    }

    void feed(bool level, uint32_t duration) override {
        ManchesterEvent event = level_and_duration_to_event(!level, duration);

        // low-level bit sequence decoding
        if (event == ManchesterEventReset) {
            parser_step = Oregon3DecoderStepReset;
            prev_bit = false;
            decode_data = 0UL;
            decode_count_bit = 0;
        }
        if (FProtoGeneral::manchester_advance(
                manchester_saved_state, event, &manchester_saved_state, &prev_bit)) {
            subghz_protocol_blocks_add_bit(prev_bit);
        }

        switch (parser_step) {
            case Oregon3DecoderStepReset:
                // waiting for fixed oregon3 preamble
                if (decode_count_bit >= OREGON3_PREAMBLE_BITS &&
                    ((decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) {
                    parser_step = Oregon3DecoderStepFoundPreamble;
                    decode_count_bit = 0;
                    decode_data = 0UL;
                }
                break;
            case Oregon3DecoderStepFoundPreamble:
                // waiting for fixed oregon3 data
                if (decode_count_bit == OREGON3_FIXED_PART_BITS) {
                    data = decode_data;
                    data_count_bit = decode_count_bit;
                    decode_data = 0UL;
                    decode_count_bit = 0;

                    // reverse nibbles in decoded data as oregon v3.0 is LSB first
                    data = (data & 0x55555555) << 1 |
                           (data & 0xAAAAAAAA) >> 1;
                    data = (data & 0x33333333) << 2 |
                           (data & 0xCCCCCCCC) >> 2;

                    ws_oregon3_decode_const_data();
                    var_bits =
                        oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(data));

                    if (!var_bits) {
                        // sensor is not supported, stop decoding
                        parser_step = Oregon3DecoderStepReset;
                    } else {
                        parser_step = Oregon3DecoderStepVarData;
                    }
                }
                break;
            case Oregon3DecoderStepVarData:
                // waiting for variable (sensor-specific data)
                if (decode_count_bit == (uint32_t)var_bits + OREGON3_CHECKSUM_BITS) {
                    var_data = decode_data & 0xFFFFFFFFFFFFFFFF;

                    // reverse nibbles in var data
                    var_data = (var_data & 0x5555555555555555) << 1 |
                               (var_data & 0xAAAAAAAAAAAAAAAA) >> 1;
                    var_data = (var_data & 0x3333333333333333) << 2 |
                               (var_data & 0xCCCCCCCCCCCCCCCC) >> 2;

                    ws_oregon3_decode_var_data(OREGON3_SENSOR_ID(data), var_data >> OREGON3_CHECKSUM_BITS);
                    parser_step = Oregon3DecoderStepReset;
                    if (callback) callback(this);
                }
                break;
        }
    }

   protected:
    // timing values
    uint32_t te_short = 500;
    uint32_t te_long = 1100;
    uint32_t te_delta = 300;
    uint32_t min_count_bit_for_found = 32;

    bool prev_bit = false;
    uint8_t var_bits{0};
    uint64_t var_data{0};

    ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) {
        bool is_long = false;

        if (DURATION_DIFF(duration, te_long) < te_delta) {
            is_long = true;
        } else if (DURATION_DIFF(duration, te_short) < te_delta) {
            is_long = false;
        } else {
            return ManchesterEventReset;
        }

        if (level)
            return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh;
        else
            return is_long ? ManchesterEventLongLow : ManchesterEventShortLow;
    }
    uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) {
        switch (sensor_id) {
            case ID_THGR221:
                // nibbles: temp + hum + '0'
                return (4 + 2 + 1) * 4;
            default:
                return 0;
        }
    }
    void ws_oregon3_decode_const_data() {
        id = OREGON3_SENSOR_ID(data);
        channel = (data >> 12) & 0xF;
        battery_low = (data & OREGON3_FLAG_BAT_LOW) ? 1 : 0;
    }
    uint16_t ws_oregon3_bcd_decode_short(uint32_t data) {
        return (data & 0xF) * 10 + ((data >> 4) & 0xF);
    }
    float ws_oregon3_decode_temp(uint32_t data) {
        int32_t temp_val;
        temp_val = ws_oregon3_bcd_decode_short(data >> 4);
        temp_val *= 10;
        temp_val += (data >> 12) & 0xF;
        if (data & 0xF) temp_val = -temp_val;
        return (float)temp_val / 10.0;
    }
    void ws_oregon3_decode_var_data(uint16_t sensor_id, uint32_t data) {
        switch (sensor_id) {
            case ID_THGR221:
            default:
                humidity = ws_oregon3_bcd_decode_short(data >> 4);
                temp = ws_oregon3_decode_temp(data >> 12);
                break;
        }
    }
};

#endif