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

#include "boost/ut.hpp"

int main() {
	using namespace boost::ut;
	using namespace std::chrono_literals;

	"roi_filter"_test = [] {
		cv::Rect roi(40, 40, 10, 10);
		auto events = dv::data::generate::eventTestSet(1000, cv::Size(100, 100));

		auto filter = dv::EventRegionFilter<>(roi);
		filter.accept(events);

		dv::EventStore filtered = filter.generateEvents();
		expect(eq(dv::boundingRect(filtered), roi));
	};

	"polarity_filter"_test = [] {
		auto events = dv::data::generate::eventTestSet(1000, cv::Size(100, 100));

		{
			auto filter = dv::EventPolarityFilter<>(true);
			filter.accept(events);
			dv::EventStore filtered = filter.generateEvents();

			expect(eq(filtered.size(), events.size()));
		}

		{
			auto filter = dv::EventPolarityFilter<>(false);
			filter.accept(events);
			dv::EventStore filtered = filter.generateEvents();

			expect(eq(filtered.size(), 0));
		}
	};

	"filter_chain"_test = [] {
		dv::EventFilterChain<> filterChain;
		cv::Rect roi(40, 40, 10, 10);

		filterChain.addFilter(std::make_unique<dv::EventPolarityFilter<>>(true));
		filterChain.addFilter(std::make_unique<dv::EventRegionFilter<>>(roi));

		auto events = dv::data::generate::eventTestSet(1000, cv::Size(100, 100));

		filterChain.accept(events);

		dv::EventStore filtered = filterChain.generateEvents();
		expect(eq(dv::boundingRect(filtered), roi));
	};

	"mask_filter"_test = [] {
		cv::Size resolution(100, 100);
		auto events = dv::data::generate::eventTestSet(1000, resolution);

		cv::Mat mask(resolution, CV_8UC1, cv::Scalar(0));
		cv::Rect roi(0, 0, resolution.width / 2, resolution.height / 2);
		cv::rectangle(mask, roi, cv::Scalar(255), cv::FILLED);

		dv::EventMaskFilter<> filter(mask);
		filter.accept(events);
		dv::EventStore filtered = filter.generateEvents();

		// The filtered data should be within top left quarter
		cv::Rect filteredRoi = dv::boundingRect(filtered);
		expect(le(filteredRoi.br().x, roi.br().x));
		expect(le(filteredRoi.br().y, roi.br().y));

		// Functional type call should result in exactly the same results
		dv::EventStore filteredFunctional;
		dv::maskFilter(events, filteredFunctional, mask);
		cv::Rect filteredFunctionalRoi = dv::boundingRect(filteredFunctional);
		expect(eq(filteredRoi, filteredFunctionalRoi));

		// Wrong mask type should throw an expception
		expect(throws([resolution] {
			cv::Mat mask(resolution, CV_32FC1, cv::Scalar(0));
			dv::EventMaskFilter<> filter(mask);
		}));
	};
}
