#pragma once

#include "../../../include/dv-processing/core/filters.hpp"
#include "../../../include/dv-processing/noise/background_activity_noise_filter.hpp"
#include "../../../include/dv-processing/noise/fast_decay_noise_filter.hpp"
#include "../../../include/dv-processing/noise/frequency_filters.hpp"
#include "../../../include/dv-processing/noise/k_noise_filter.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

/**
 * @brief Binds the event filters to python.
 * @param m The module to bind to.
 */
void bind_filters(py::module &m) {
	py::class_<dv::EventFilterBase<>, std::shared_ptr<dv::EventFilterBase<>>> m_eventFilterBase(m, "EventFilterBase");

	py::class_<dv::EventRegionFilter<>, std::shared_ptr<dv::EventRegionFilter<>>>(
		m, "EventRegionFilter", "Event filter that filters events based on a given ROI.")
		.def(py::init<const cv::Rect &>(), py::arg("roi"))
		.def("accept", &dv::EventRegionFilter<>::accept, py::arg("events"), "Accepts incoming events.")
		.def("generateEvents", &dv::EventRegionFilter<>::generateEvents,
			"Apply the filter algorithm and return only the filtered events from the ones that were accepted as input.")
		.def("retain", &dv::EventRegionFilter<>::retain, "Test whether event belongs to an ROI.")
		.def("getNumberOfIncomingEvents", &dv::EventRegionFilter<>::getNumberOfIncomingEvents,
			"Get number of total events that were accepted by the noise filter.")
		.def("getNumberOfOutgoingEvents", &dv::EventRegionFilter<>::getNumberOfOutgoingEvents,
			"Total number of outgoing events from this filter instance.")
		.def("getReductionFactor", &dv::EventRegionFilter<>::getReductionFactor,
			"Get the reduction factor of this filter. It's a fraction representation of events that were discard by "
			"this filter compared to the amount of incoming events.");

	py::class_<dv::EventPolarityFilter<>, std::shared_ptr<dv::EventPolarityFilter<>>>(
		m, "EventPolarityFilter", "Event filter based on polarity.")
		.def(py::init<bool>(), py::arg("polarity"))
		.def("accept", &dv::EventPolarityFilter<>::accept, py::arg("events"), "Accepts incoming events.")
		.def("generateEvents", &dv::EventPolarityFilter<>::generateEvents,
			"Apply the filter algorithm and return only the filtered events from the ones that were accepted as input.")
		.def("retain", &dv::EventPolarityFilter<>::retain, "Test whether event is of configured polarity.")
		.def("getNumberOfIncomingEvents", &dv::EventPolarityFilter<>::getNumberOfIncomingEvents,
			"Get number of total events that were accepted by the noise filter.")
		.def("getNumberOfOutgoingEvents", &dv::EventPolarityFilter<>::getNumberOfOutgoingEvents,
			"Total number of outgoing events from this filter instance.")
		.def("getReductionFactor", &dv::EventPolarityFilter<>::getReductionFactor,
			"Get the reduction factor of this filter. It's a fraction representation of events that were discard by "
			"this filter compared to the amount of incoming events.");

	py::class_<dv::EventMaskFilter<>, std::shared_ptr<dv::EventMaskFilter<>>>(
		m, "EventMaskFilter", "Event filter that applies a mask.")
		.def(py::init<cv::Mat>(), py::arg("mask"),
			"Create an event masking filter. Discards any events that happen on coordinates where mask has a zero "
			"value and retains all events with coordinates where mask has a non-zero value. The mask to requires to be "
			"unsigned 8bit integer single channel array.")
		.def("accept", &dv::EventMaskFilter<>::accept, py::arg("events"), "Accepts incoming events.")
		.def("generateEvents", &dv::EventMaskFilter<>::generateEvents,
			"Apply the filter algorithm and return only the filtered events from the ones that were accepted as input.")
		.def("retain", &dv::EventMaskFilter<>::retain, "Test whether event is of configured polarity.")
		.def("getNumberOfIncomingEvents", &dv::EventMaskFilter<>::getNumberOfIncomingEvents,
			"Get number of total events that were accepted by the noise filter.")
		.def("getNumberOfOutgoingEvents", &dv::EventMaskFilter<>::getNumberOfOutgoingEvents,
			"Total number of outgoing events from this filter instance.")
		.def("getReductionFactor", &dv::EventMaskFilter<>::getReductionFactor,
			"Get the reduction factor of this filter. It's a fraction representation of events that were discard by "
			"this filter compared to the amount of incoming events.")
		.def("getMask", &dv::EventMaskFilter<>::getMask, "Get the mask that is currently applied.")
		.def("setMask", &dv::EventMaskFilter<>::setMask, py::arg("mask"), "Set a new mask to this filter.");

	py::class_<dv::EventFilterChain<>, std::shared_ptr<dv::EventFilterChain<>>>(m, "EventFilterChain",
		"Event filter based on multiple event filter applied sequentially. Internally stores any added filters and")
		.def(py::init<>())
		.def("accept", &dv::EventFilterChain<>::accept, py::arg("events"), "Accepts incoming events.")
		.def("generateEvents", &dv::EventFilterChain<>::generateEvents,
			"Apply the filter algorithm and return only the filtered events from the ones that were accepted as input.")
		.def("retain", &dv::EventFilterChain<>::retain, "Test whether event belongs to an ROI.")
		.def("getNumberOfIncomingEvents", &dv::EventFilterChain<>::getNumberOfIncomingEvents,
			"Get number of total events that were accepted by the noise filter.")
		.def("getNumberOfOutgoingEvents", &dv::EventFilterChain<>::getNumberOfOutgoingEvents,
			"Total number of outgoing events from this filter instance.")
		.def("getReductionFactor", &dv::EventFilterChain<>::getReductionFactor,
			"Get the reduction factor of this filter. It's a fraction representation of events that were discard by "
			"this filter compared to the amount of incoming events.")
		.def("addFilter", &dv::EventFilterChain<>::addFilter, "Add a filter to the chain of filtering.")
		// Ugh, I need to list all available filter types.
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::LowPassFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::HighPassFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::BandPassFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::BandCutFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::EventRegionFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::EventPolarityFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self,
				const std::shared_ptr<dv::noise::BackgroundActivityNoiseFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::KNoiseFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.")
		.def(
			"addFilter",
			[](dv::EventFilterChain<> &self, const std::shared_ptr<dv::noise::FastDecayNoiseFilter<>> &filter) {
				self.addFilter(filter);
			},
			"Add a filter to the chain of filtering.");
}
