#include "../../include/dv-processing/core/event.hpp"
#include "../../include/dv-processing/data/generate.hpp"
#include "../../include/dv-processing/noise/frequency_filters.hpp"

#include "boost/ut.hpp"

using namespace boost::ut;

[[nodiscard]] static dv::EventStore generateLineEvents(
	const cv::Size &resolution, const float frequency, int64_t &timestamp, const size_t numberOfPeriods = 10ULL) {
	const auto period = static_cast<int64_t>(1e6f / frequency);

	dv::EventStore events;
	for (size_t i = 0; i < numberOfPeriods; ++i) {
		static const size_t lineSize  = 50;
		timestamp                    += period;
		events                       += dv::data::generate::eventLine(
			timestamp, cv::Point(0, 0), cv::Point(resolution.width - 1, resolution.height - 1), lineSize);
	}

	return events;
}

int main() {
	"low_pass_filter"_test = [] {
		const cv::Size resoluton(100, 100);
		const float cutOffFrequency = 100.0f; // In Hz
		auto lowPassFilter          = dv::noise::LowPassFilter<>(resoluton, cutOffFrequency);

		// Generate events with a frequency lower than the cutoff frequency, these should not be filtered out
		int64_t initialTimestamp = 100'000LL;
		dv::EventStore events    = generateLineEvents(resoluton, cutOffFrequency - 20.0f, initialTimestamp);
		lowPassFilter.accept(events);
		expect(eq(lowPassFilter.generateEvents().size(), events.size()));

		// Generate events with a frequency higher than the cutoff frequency, these should be filtered out
		events = generateLineEvents(resoluton, cutOffFrequency + 20.0f, initialTimestamp);
		lowPassFilter.accept(events);
		expect(eq(lowPassFilter.generateEvents().size(), 0ULL));
	};

	"high_pass_filter"_test = [] {
		const cv::Size resoluton(100, 100);
		const float cutOffFrequency = 100.0f; // In Hz
		auto highPassFilter         = dv::noise::HighPassFilter<>(resoluton, cutOffFrequency);

		// Generate events with a frequency lower than the cutoff frequency, these should be filtered out
		int64_t initialTimestamp = 100'000LL;
		dv::EventStore events    = generateLineEvents(resoluton, cutOffFrequency - 20.0f, initialTimestamp);
		highPassFilter.accept(events);
		expect(eq(highPassFilter.generateEvents().size(), 0ULL));

		// Generate events with a frequency higher than the cutoff frequency, these should not be filtered out
		events = generateLineEvents(resoluton, cutOffFrequency + 20.0f, initialTimestamp);
		highPassFilter.accept(events);
		expect(eq(highPassFilter.generateEvents().size(), events.size()));
	};

	"band_pass_filter"_test = [] {
		const cv::Size resoluton(100, 100);
		const float lowerCutOffFrequency = 100.0f; // In Hz
		const float upperCutOffFrequency = 200.0f; // In Hz
		auto bandPassFilter = dv::noise::BandPassFilter<>(resoluton, lowerCutOffFrequency, upperCutOffFrequency);

		// Generate events with a frequency outside the band frequency range, these should be filtered out
		int64_t initialTimestamp = 100'000LL;
		dv::EventStore events    = generateLineEvents(resoluton, upperCutOffFrequency + 20.0f, initialTimestamp);
		bandPassFilter.accept(events);
		expect(eq(bandPassFilter.generateEvents().size(), 0ULL));

		// Generate events with a frequency within the band frequency range, these should not be filtered out
		events = generateLineEvents(resoluton, lowerCutOffFrequency + 20.0f, initialTimestamp);
		bandPassFilter.accept(events);
		expect(eq(bandPassFilter.generateEvents().size(), events.size()));
	};

	"band_cut_filter"_test = [] {
		const cv::Size resoluton(100, 100);
		const float lowerCutOffFrequency = 100.0f; // In Hz
		const float upperCutOffFrequency = 200.0f; // In Hz
		auto bandCutFilter = dv::noise::BandCutFilter<>(resoluton, lowerCutOffFrequency, upperCutOffFrequency);

		// Generate events with a frequency outside the band frequency range, these should not be filtered out
		int64_t initialTimestamp = 200LL;
		dv::EventStore events    = generateLineEvents(resoluton, lowerCutOffFrequency - 20.0f, initialTimestamp);
		bandCutFilter.accept(events);
		expect(eq(bandCutFilter.generateEvents().size(), events.size()));

		// Generate events with a frequency within the band frequency range, these should be filtered out
		events = generateLineEvents(resoluton, lowerCutOffFrequency + 20.0f, initialTimestamp);
		bandCutFilter.accept(events);
		expect(eq(bandCutFilter.generateEvents().size(), 0ULL));
	};

	return EXIT_SUCCESS;
}
