#pragma once

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

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::enum_<dv::features::ImageFeatureDetector::FeaturePostProcessing>(m, "FeaturePostProcessing")
		.value("NONE", dv::features::ImageFeatureDetector::FeaturePostProcessing::NONE)
		.value("TOP_N", dv::features::ImageFeatureDetector::FeaturePostProcessing::TOP_N)
		.value("ADAPTIVE_NMS", dv::features::ImageFeatureDetector::FeaturePostProcessing::ADAPTIVE_NMS);

	py::class_<dv::features::ImageFeatureDetector>(m, "ImageFeatureDetector",
		"A base class to implement feature detectors on different input types, specifically either images, time "
		"surfaces, or event stores. The implementing class should override the `detect` function and output a vector "
		"of unordered features with a quality score. The API will handle margin calculations and post processing of "
		"the features.")
		.def(py::init([](const cv::Size &resolution) {
			return dv::features::ImageFeatureDetector(resolution, cv::GFTTDetector::create());
		}),
			"resolution"_a,
			"Create a detector that uses good-features-to-track (GFTT) algorithm for feature detection. This "
			"constructor defaults post-processing step to none as GFTT does NMS already and a margin coefficient value "
			"of 0.02.")
		.def("runDetection", &dv::features::ImageFeatureDetector::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", &dv::features::ImageFeatureDetector::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", &dv::features::ImageFeatureDetector::getPostProcessing,
			"Get the type of post-processing.")
		.def("setPostProcessing", &dv::features::ImageFeatureDetector::setPostProcessing, py::arg("postProcessing"),
			"Set the type of post-processing.")
		.def("getMargin", &dv::features::ImageFeatureDetector::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", &dv::features::ImageFeatureDetector::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", &dv::features::ImageFeatureDetector::isWithinROI, py::arg("point"),
			"Check whether a point belongs to the ROI without the margins.")
		.def("getImageDimensions", &dv::features::ImageFeatureDetector::getImageDimensions,
			"Get configured image dimensions.");

	py::class_<dv::features::ImagePyrFeatureDetector>(m, "ImagePyrFeatureDetector",
		"A base class to implement feature detectors on different input types, specifically either images, time "
		"surfaces, or event stores. The implementing class should override the `detect` function and output a vector "
		"of unordered features with a quality score. The API will handle margin calculations and post processing of "
		"the features.")
		.def(py::init([](const cv::Size &resolution) {
			return dv::features::ImagePyrFeatureDetector(resolution, cv::GFTTDetector::create());
		}),
			"resolution"_a,
			"Create a detector that uses good-features-to-track (GFTT) algorithm for feature detection. This "
			"constructor defaults post-processing step to none as GFTT does NMS already and a margin coefficient value "
			"of 0.02.")
		.def("runDetection", &dv::features::ImagePyrFeatureDetector::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", &dv::features::ImagePyrFeatureDetector::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",
			[](const dv::features::ImagePyrFeatureDetector &self)
				-> dv::features::ImageFeatureDetector::FeaturePostProcessing {
				return static_cast<dv::features::ImageFeatureDetector::FeaturePostProcessing>(self.getPostProcessing());
			},
			"Get the type of post-processing.")
		.def(
			"setPostProcessing",
			[](dv::features::ImagePyrFeatureDetector &self,
				const dv::features::ImageFeatureDetector::FeaturePostProcessing postProcessing) {
				self.setPostProcessing(
					static_cast<dv::features::ImagePyrFeatureDetector::FeaturePostProcessing>(postProcessing));
			},
			py::arg("postProcessing"), "Set the type of post-processing.")
		.def("getMargin", &dv::features::ImagePyrFeatureDetector::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", &dv::features::ImagePyrFeatureDetector::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", &dv::features::ImagePyrFeatureDetector::isWithinROI, py::arg("point"),
			"Check whether a point belongs to the ROI without the margins.")
		.def("getImageDimensions", &dv::features::ImagePyrFeatureDetector::getImageDimensions,
			"Get configured image dimensions.");
}
