#ifndef __FPROTO_OREGONv1_H__
#define __FPROTO_OREGONv1_H__

#include "weatherbase.hpp"

typedef enum {
    Oregon_V1DecoderStepReset = 0,
    Oregon_V1DecoderStepFoundPreamble,
    Oregon_V1DecoderStepParse,
} Oregon_V1DecoderStep;

#define OREGON_V1_HEADER_OK 0xFF

class FProtoWeatherOregonV1 : public FProtoWeatherBase {
   public:
    FProtoWeatherOregonV1() {
        sensorType = FPW_OREGONv1;
    }

    void feed(bool level, uint32_t duration) override {
        ManchesterEvent event = ManchesterEventReset;
        switch (parser_step) {
            case Oregon_V1DecoderStepReset:
                if ((level) && (DURATION_DIFF(duration, te_short) <
                                te_delta)) {
                    parser_step = Oregon_V1DecoderStepFoundPreamble;
                    te_last = duration;
                    header_count = 0;
                }
                break;
            case Oregon_V1DecoderStepFoundPreamble:
                if (level) {
                    // keep high levels, if they suit our durations
                    if ((DURATION_DIFF(duration, te_short) <
                         te_delta) ||
                        (DURATION_DIFF(duration, te_short * 4) <
                         te_delta)) {
                        te_last = duration;
                    } else {
                        parser_step = Oregon_V1DecoderStepReset;
                    }
                } else if (
                    // checking low levels
                    (DURATION_DIFF(duration, te_short) <
                     te_delta) &&
                    (DURATION_DIFF(te_last, te_short) <
                     te_delta)) {
                    // Found header
                    header_count++;
                } else if (
                    (DURATION_DIFF(duration, te_short * 3) <
                     te_delta) &&
                    (DURATION_DIFF(te_last, te_short) <
                     te_delta)) {
                    // check header
                    if (header_count > 7) {
                        header_count = OREGON_V1_HEADER_OK;
                    }
                } else if (
                    (header_count == OREGON_V1_HEADER_OK) &&
                    (DURATION_DIFF(te_last, te_short * 4) <
                     te_delta)) {
                    // found all the necessary patterns
                    decode_data = 0;
                    decode_count_bit = 1;
                    FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
                    parser_step = Oregon_V1DecoderStepParse;
                    if (duration < te_short * 4) {
                        first_bit = 1;
                    } else {
                        first_bit = 0;
                    }
                } else {
                    parser_step = Oregon_V1DecoderStepReset;
                }
                break;
            case Oregon_V1DecoderStepParse:
                if (level) {
                    if (DURATION_DIFF(duration, te_short) <
                        te_delta) {
                        event = ManchesterEventShortHigh;
                    } else if (
                        DURATION_DIFF(duration, te_long) <
                        te_delta) {
                        event = ManchesterEventLongHigh;
                    } else {
                        parser_step = Oregon_V1DecoderStepReset;
                    }
                } else {
                    if (DURATION_DIFF(duration, te_short) <
                        te_delta) {
                        event = ManchesterEventShortLow;
                    } else if (
                        DURATION_DIFF(duration, te_long) <
                        te_delta) {
                        event = ManchesterEventLongLow;
                    } else if (duration >= ((uint32_t)te_long * 2)) {
                        if (decode_count_bit ==
                            min_count_bit_for_found) {
                            if (first_bit) {
                                decode_data = ~decode_data | (1 << 31);
                            }
                            if (ws_protocol_oregon_v1_check()) {
                                data = decode_data;
                                data_count_bit = decode_count_bit;
                                ws_protocol_oregon_v1_remote_controller();
                                if (callback) callback(this);
                            }
                        }
                        decode_data = 0;
                        decode_count_bit = 0;
                        FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
                    } else {
                        parser_step = Oregon_V1DecoderStepReset;
                    }
                }
                if (event != ManchesterEventReset) {
                    bool bit;
                    bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);

                    if (data_ok) {
                        decode_data = (decode_data << 1) | !bit;
                        decode_count_bit++;
                    }
                }

                break;
        }
    }

   protected:
    // timing values
    uint32_t te_short = 1465;
    uint32_t te_long = 2930;
    uint32_t te_delta = 350;
    uint32_t min_count_bit_for_found = 32;

    uint8_t first_bit{0};

    bool ws_protocol_oregon_v1_check() {
        if (!decode_data) return false;
        uint64_t data = FProtoGeneral::subghz_protocol_blocks_reverse_key(decode_data, 32);
        uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff);
        crc = (crc & 0xff) + ((crc >> 8) & 0xff);
        return (crc == ((data >> 24) & 0xFF));
    }
    void ws_protocol_oregon_v1_remote_controller() {
        uint64_t data2 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 32);

        id = data2 & 0xFF;
        channel = ((data2 >> 6) & 0x03) + 1;

        float temp_raw =
            ((data2 >> 8) & 0x0F) * 0.1f + ((data2 >> 12) & 0x0F) + ((data2 >> 16) & 0x0F) * 10.0f;
        if (!((data2 >> 21) & 1)) {
            temp = temp_raw;
        } else {
            temp = -temp_raw;
        }
        battery_low = !((data2 >> 23) & 1ULL);
        btn = WS_NO_BTN;
        humidity = WS_NO_HUMIDITY;
    }
};

#endif