#include "IrFoxAnalyzer.h"
#include "IrFoxAnalyzerSettings.h"
#include "IrFoxDecoder.h"
#include <AnalyzerChannelData.h>
#include <AnalyzerResults.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

IrFoxAnalyzer::IrFoxAnalyzer()
	: Analyzer2(),
	  mSettings(),
	  mSimulationInitilized(false)
{
	SetAnalyzerSettings(&mSettings);
	UseFrameV2();
}

IrFoxAnalyzer::~IrFoxAnalyzer()
{
	KillThread();
}

void IrFoxAnalyzer::SetupResults()
{
	m_packet_hex_by_frame.clear();
	mResults.reset(new IrFoxAnalyzerResults(this, &mSettings));
	SetAnalyzerResults(mResults.get());
	mResults->AddChannelBubblesWillAppearOn(mSettings.mInputChannel);
}

static void append_hex(std::string& s, const uint8_t* p, size_t n, size_t max_bytes = 64)
{
	static const char* hd = "0123456789abcdef";
	const size_t m = n < max_bytes ? n : max_bytes;
	for (size_t i = 0; i < m; i++)
	{
		s.push_back(hd[p[i] >> 4]);
		s.push_back(hd[p[i] & 0xFu]);
		if (i + 1 < m)
			s.push_back(' ');
	}
	if (n > max_bytes)
		s += "...";
}

const char* IrFoxAnalyzer::PacketHexForFrame(U64 frame_id)
{
	auto it = m_packet_hex_by_frame.find(frame_id);
	if (it == m_packet_hex_by_frame.end())
		return "";
	m_hex_scratch = it->second;
	return m_hex_scratch.c_str();
}

const char* IrFoxAnalyzer::BubbleTextForFrame(U64 frame_id) const
{
	auto it = m_bubble_text_by_frame.find(frame_id);
	if (it == m_bubble_text_by_frame.end())
		return "";
	m_bubble_scratch = it->second;
	return m_bubble_scratch.c_str();
}

void IrFoxAnalyzer::WorkerThread()
{
	mIr = GetAnalyzerChannelData(mSettings.mInputChannel);
	m_packet_hex_by_frame.clear();
	m_bubble_text_by_frame.clear();

	const U32 fs = GetSampleRate();
	IrFoxDecoder decoder;
	decoder.reset();

	/** Потоковый фильтр: убирает импульсы короче kMinFilteredPulseUs (иголки/дребезг в сэмплах). */
	const U64 min_seg_samples =
		std::max<U64>(1ULL, static_cast<U64>((static_cast<double>(irfox::kMinFilteredPulseUs) * 1e-6) * static_cast<double>(fs) + 0.5));
	struct RawEdge
	{
		U64 sample;
		bool rising;
	};
	std::vector<RawEdge> pending;
	U64 last_dec_edge_sample = 0;
	bool last_dec_edge_valid = false;

	auto collapse_short_pairs = [&]() {
		for (size_t i = 0; i + 1 < pending.size();)
		{
			if (pending[i + 1].sample - pending[i].sample < min_seg_samples)
			{
				pending.erase(pending.begin() + static_cast<std::ptrdiff_t>(i),
							  pending.begin() + static_cast<std::ptrdiff_t>(i + 2));
				if (i > 0)
					--i;
			}
			else
				++i;
		}
	};

	auto strip_vs_last_decoder = [&]() {
		for (;;)
		{
			collapse_short_pairs();
			if (!last_dec_edge_valid || pending.empty())
				return;
			if (pending[0].sample - last_dec_edge_sample >= min_seg_samples)
				return;
			pending.erase(pending.begin());
		}
	};

	U32 frames_since_commit = 0;
	const U32 kCommitBatch = 256;

	IrFoxOnBit on_bit = [&](const IrFoxEmitBit& e) {
		Frame frame;
		frame.mStartingSampleInclusive = static_cast<S64>(e.start_sample);
		frame.mEndingSampleInclusive = static_cast<S64>(e.end_sample);
		frame.mType = e.frame_type;
		frame.mData1 = e.bit_value;
		frame.mData2 = e.bit_index | (U64(e.err_low) << 16) | (U64(e.err_high) << 24) | (U64(e.err_other) << 32);
		frame.mFlags = e.mflags;
		// В SDK только ERROR/WARNING меняют цвет бабла; sync выделяем янтарным (как warning), данные — обычные.
		if (e.frame_type == IRF_FT_SYNC_BIT)
			frame.mFlags |= DISPLAY_AS_WARNING_FLAG;

		const U64 fid = mResults->AddFrame(frame);
		if (e.bubble_text[0] != '\0')
			m_bubble_text_by_frame[fid] = e.bubble_text;
		if (++frames_since_commit >= kCommitBatch)
		{
			mResults->CommitResults();
			frames_since_commit = 0;
		}
	};

	IrFoxOnPacket on_pkt = [&](const IrFoxEmitPacket& p) {
		Frame frame;
		frame.mStartingSampleInclusive = static_cast<S64>(p.start_sample);
		frame.mEndingSampleInclusive = static_cast<S64>(p.end_sample);
		frame.mType = p.crc_ok ? IRF_FT_PACKET_OK : IRF_FT_PACKET_CRC_FAIL;
		frame.mData1 = p.pack_size;
		frame.mData2 = (U64(p.err_low) << 0) | (U64(p.err_high) << 8) | (U64(p.err_other) << 16);
		if (!p.crc_ok)
			frame.mFlags |= DISPLAY_AS_ERROR_FLAG;

		const U64 fid = mResults->AddFrame(frame);

		std::string hx;
		append_hex(hx, p.data_bytes, p.pack_size);
		m_packet_hex_by_frame[fid] = std::move(hx);

		FrameV2 fv2;
		fv2.AddBoolean("crc_ok", p.crc_ok);
		fv2.AddInteger("len", static_cast<S64>(p.pack_size));
		fv2.AddInteger("err_low", static_cast<S64>(p.err_low));
		fv2.AddInteger("err_high", static_cast<S64>(p.err_high));
		fv2.AddInteger("err_other", static_cast<S64>(p.err_other));
		fv2.AddByteArray("data", p.data_bytes, p.pack_size);
		mResults->AddFrameV2(fv2, p.crc_ok ? "packet_ok" : "packet_bad", static_cast<U64>(p.start_sample),
							  static_cast<U64>(p.end_sample));

		if (++frames_since_commit >= kCommitBatch)
		{
			mResults->CommitResults();
			frames_since_commit = 0;
		}
	};

	auto emit_confirmed_edges = [&]() {
		for (;;)
		{
			collapse_short_pairs();
			strip_vs_last_decoder();
			if (pending.size() < 2)
				return;
			if (pending[1].sample - pending[0].sample < min_seg_samples)
				continue;
			decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
			last_dec_edge_sample = pending[0].sample;
			last_dec_edge_valid = true;
			pending.erase(pending.begin());
		}
	};

	auto flush_pending_tail = [&]() {
		collapse_short_pairs();
		strip_vs_last_decoder();
		while (pending.size() >= 2 && pending[1].sample - pending[0].sample >= min_seg_samples)
		{
			decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
			last_dec_edge_sample = pending[0].sample;
			last_dec_edge_valid = true;
			pending.erase(pending.begin());
			collapse_short_pairs();
			strip_vs_last_decoder();
		}
		if (pending.size() == 1)
		{
			decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
			last_dec_edge_sample = pending[0].sample;
			last_dec_edge_valid = true;
			pending.clear();
		}
	};

	for (;;)
	{
		CheckIfThreadShouldExit();

		const U64 segment_start = mIr->GetSampleNumber();
		const BitState level = mIr->GetBitState();

		mIr->AdvanceToNextEdge();

		const U64 edge_sample = mIr->GetSampleNumber();
		if (edge_sample == segment_start)
			break;

		const BitState new_level = mIr->GetBitState();
		const bool rising = (new_level == BIT_HIGH);

		pending.push_back(RawEdge{edge_sample, rising});
		emit_confirmed_edges();
		ReportProgress(edge_sample);
	}

	flush_pending_tail();
	decoder.flushEnd(mIr->GetSampleNumber(), fs, on_bit, on_pkt);

	if (frames_since_commit != 0)
		mResults->CommitResults();
}

bool IrFoxAnalyzer::NeedsRerun()
{
	return false;
}

U32 IrFoxAnalyzer::GenerateSimulationData(U64 minimum_sample_index, U32 device_sample_rate,
										  SimulationChannelDescriptor** simulation_channels)
{
	if (mSimulationInitilized == false)
	{
		mSimulationDataGenerator.Initialize(GetSimulationSampleRate(), &mSettings);
		mSimulationInitilized = true;
	}

	return mSimulationDataGenerator.GenerateSimulationData(minimum_sample_index, device_sample_rate,
															simulation_channels);
}

U32 IrFoxAnalyzer::GetMinimumSampleRateHz()
{
	return 200000;
}

const char* IrFoxAnalyzer::GetAnalyzerName() const
{
	return "IR Fox";
}

const char* GetAnalyzerName()
{
	return "IR Fox";
}

Analyzer* CreateAnalyzer()
{
	return new IrFoxAnalyzer();
}

void DestroyAnalyzer(Analyzer* analyzer)
{
	delete analyzer;
}
