#pragma once

#include "../../../include/dv-processing/features/event_blob_detector.hpp"
#include "../../../include/dv-processing/features/feature_detector.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

/**
 * @brief Bind features::EventBlobDetector and its FeatureDetector to python
 * @param m pybind The module to bind to
 */
void bind_event_blob_detector(py::module &m) {
	using pybind11::operator""_a;

	py::class_<dv::features::EventBlobDetector>(
		m, "EventBlobDetector", "Event-based blob detector performing detection on accumulated event images.")
		.def(py::init(
				 [](const cv::Size &resolution, const int pyramidLevel, std::function<cv::Mat(cv::Mat &)> preprocess) {
					 return dv::features::EventBlobDetector(resolution, pyramidLevel, [preprocess](cv::Mat &image) {
						 image = preprocess(image);
					 });
				 }),
			"resolution"_a, "pyramidLevel"_a = 0,
			"preprocess"_a = std::function<cv::Mat(cv::Mat &)>([](cv::Mat &image) {
				return image;
			}),
			"Please note that function signature for preprocessing (in python) expect a matrix to be returned.")
		.def("detect", &dv::features::EventBlobDetector::detect, py::arg("events"), py::arg("roi") = cv::Rect(),
			py::arg("mask") = cv::Mat(), "Detection step.");

	using EventFeatureBlobDetector = dv::features::FeatureDetector<dv::EventStore, dv::features::EventBlobDetector>;
	py::class_<EventFeatureBlobDetector>(m, "EventFeatureBlobDetector")
		.def(py::init([](const cv::Size &resolution, const int pyramidLevel,
						  std::function<cv::Mat(cv::Mat &)> preprocess) {
			return EventFeatureBlobDetector(resolution, std::make_shared<dv::features::EventBlobDetector>(
															resolution, pyramidLevel, [preprocess](cv::Mat &image) {
																image = preprocess(image);
															}));
		}),
			"resolution"_a, "pyramidLevel"_a = 0,
			"preprocess"_a = std::function<cv::Mat(cv::Mat &)>([](cv::Mat &image) {
				return image;
			}))

		.def("runDetection", &EventFeatureBlobDetector::runDetection, py::arg("input"), py::arg("numPoints"),
			py::arg("mask") = cv::Mat(),
			"Public detection call. Calls the overloaded `detect` function, applies margin and post processing.")
		.def("runRedetection", &EventFeatureBlobDetector::runRedetection, py::arg("prior"), py::arg("input"),
			py::arg("numPoints"), py::arg("mask") = cv::Mat(),
			"Redetect new features and add them to already detected features. This function performs detection within "
			"masked region (if mask is non-empty), runs postprocessing and appends the additional features to the "
			"prior keypoint list.")
		.def("getPostProcessing", &EventFeatureBlobDetector::getPostProcessing, "Get the type of post-processing.")
		.def("setPostProcessing", &EventFeatureBlobDetector::setPostProcessing, py::arg("postProcessing"),
			"Set the type of post-processing.")
		.def("getMargin", &EventFeatureBlobDetector::getMargin,
			"Get currently applied margin coefficient. Margin coefficient is multiplied by the width and height of the "
			"image to calculate an adaptive border alongside the edges of image, where features should not be "
			"detected.")
		.def("setMargin", &EventFeatureBlobDetector::setMargin, py::arg("margin"),
			"Set the margin coefficient. Margin coefficient is multiplied by the width and height of the image to "
			"calculate an adaptive border alongside the edges of image, where features should not be detected.")
		.def("isWithinROI", &EventFeatureBlobDetector::isWithinROI, py::arg("point"),
			"Check whether a point belongs to the ROI without the margins.")
		.def("getImageDimensions", &EventFeatureBlobDetector::getImageDimensions, "Get configured image dimensions.");
}
