#pragma once

#include "IrFoxProtocolConstants.h"
#include <cstdint>
#include <functional>

enum IrFoxFrameType : uint8_t
{
	IRF_FT_DATA_BIT = 1,
	IRF_FT_SYNC_BIT = 2,
	IRF_FT_PACKET_OK = 3,
	IRF_FT_PACKET_CRC_FAIL = 4,
	IRF_FT_OVERFLOW = 5,
	IRF_FT_ABORT = 6,
	IRF_FT_PREAMBLE = 7,
};

/** WithBubble — вызвать on_bit; Quiet — только обновить состояние (для пакета битов с одного фронта). */
enum class IrFoxEmitBitMode : uint8_t
{
	WithBubble,
	Quiet,
};

struct IrFoxEmitBit
{
	int64_t start_sample;
	int64_t end_sample;
	uint8_t frame_type;
	uint64_t bit_value;
	uint64_t bit_index;
	uint8_t mflags;
	bool invert_fix;
	uint8_t err_low;
	uint8_t err_high;
	uint8_t err_other;
	/** Подпись бабла: "0", "1" или склейка "001"; пусто — смотреть bit_value. */
	char bubble_text[32]{};
};

struct IrFoxEmitPacket
{
	int64_t start_sample;
	int64_t end_sample;
	bool crc_ok;
	uint8_t pack_size;
	uint8_t err_low;
	uint8_t err_high;
	uint8_t err_other;
	uint8_t data_bytes[irfox::kDataByteSizeMax];
};

using IrFoxOnBit = std::function<void(const IrFoxEmitBit&)>;
using IrFoxOnPacket = std::function<void(const IrFoxEmitPacket&)>;

class IrFoxDecoder
{
public:
	void reset();
	void processEdge(uint64_t sample, bool rising, uint32_t sample_rate_hz, const IrFoxOnBit& on_bit,
					  const IrFoxOnPacket& on_pkt);
	void flushEnd(uint64_t last_sample, uint32_t sample_rate_hz, const IrFoxOnBit& on_bit, const IrFoxOnPacket& on_pkt);

private:
	static uint16_t ceil_div_u16(uint16_t val, uint16_t divider);
	static uint8_t crc8(const uint8_t* data, uint8_t start, uint8_t end, uint8_t poly);
	bool crc_check(uint8_t len, uint16_t& crc_out);

	void first_rx();
	void listen_start(double t_us);
	void check_timeout(double t_us);
	void write_to_buffer(bool bit, bool pack_trace_invert_fix, uint64_t cell_start_s, uint64_t cell_end_s,
						 const IrFoxOnBit& on_bit, const IrFoxOnPacket& on_pkt,
						 IrFoxEmitBitMode emit_mode = IrFoxEmitBitMode::WithBubble);

	double sample_to_us(uint64_t sample, uint32_t fs) const { return double(sample) * 1e6 / double(fs); }

	// --- state (mirror IR_DecoderRaw) ---
	uint8_t data_buffer[irfox::kDataByteSizeMax]{};
	uint8_t err_low_signal = 0;
	uint8_t err_high_signal = 0;
	uint8_t err_other = 0;

	bool is_available = false;
	uint16_t pack_size = 0;
	uint16_t crc_value = 0;
	bool is_recive = false;
	bool is_recive_raw = false;
	bool is_preamb = false;
	bool is_buffer_overflow = false;
	bool is_wrong_pack = false;

	uint32_t rise_sync_time_us = irfox::kBitTimeUs;

	double prev_rise_us = 0;
	double prev_fall_us = 0;
	uint64_t prev_rise_sample = 0;
	uint64_t prev_fall_sample = 0;
	/** Сэмпл предыдущего нарастающего фронта до обновления на текущем тике (граница ячейки бита, см. IR_DecoderRaw::tick). */
	uint64_t rise_period_anchor_sample_ = 0;
	uint64_t preamble_bubble_start_sample_ = 0;
	bool preamble_bubble_start_valid_ = false;
	bool trim_first_data_bit_cell_ = false;

	double last_edge_time_us = 0;
	uint64_t last_edge_sample = 0;
	double last_processed_edge_us = 0;
	bool have_last_processed = false;

	void fill_err_snapshot(IrFoxEmitBit& e) const
	{
		e.err_low = err_low_signal;
		e.err_high = err_high_signal;
		e.err_other = err_other;
	}

	uint32_t rise_period_us = 0;
	uint32_t high_time_us = 0;
	uint32_t low_time_us = 0;

	int8_t high_count = 0;
	int8_t low_count = 0;
	int8_t all_count = 0;

	uint16_t wrong_counter = 0;
	int8_t preamb_front_counter = 0;
	int16_t buf_bit_pos = 0;
	bool is_data = true;
	uint16_t i_data_buffer = 0;
	uint16_t next_control_bit = irfox::kBitPerByte;
	uint8_t i_sync_bit = 0;
	uint8_t err_sync_bit = 0;
	uint16_t error_counter = 0;
	uint8_t msg_type_receive = 0;
};
