#pragma once

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

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::features::ArcCornerDetector<>>(m, "ArcCornerDetector",
		"This class implement the Arc* corner detector presented in the following paper: "
		"https://www.research-collection.ethz.ch/bitstream/handle/20.500.11850/277131/RAL2018-camera-ready.pdf")
		.def(py::init<const cv::Size, const int64_t, const bool>(), py::arg("resolution"), py::arg("range"),
			py::arg("resetTsAtEachIteration"))
		.def("detect", &dv::features::ArcCornerDetector<>::detect, py::arg("events"), py::arg("roi"), py::arg("mask"),
			"Runs the detection algorithm.")
		.def("getTimeSurface", &dv::features::ArcCornerDetector<>::getTimeSurface, py::arg("polarity"),
			"Returns the TimeSurface for a given polarity");

	using ArcEventFeatureDetector = dv::features::FeatureDetector<dv::EventStore, dv::features::ArcCornerDetector<>>;
	py::class_<ArcEventFeatureDetector>(m, "ArcEventFeatureDetector")
		.def(py::init([](const cv::Size &resolution, const int64_t cornerRange, const bool resetTS) {
			return ArcEventFeatureDetector(
				resolution, std::make_shared<dv::features::ArcCornerDetector<>>(resolution, cornerRange, resetTS));
		}))
		.def("runDetection", &ArcEventFeatureDetector::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", &ArcEventFeatureDetector::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 ArcEventFeatureDetector &self) -> dv::features::ImageFeatureDetector::FeaturePostProcessing {
				return static_cast<dv::features::ImageFeatureDetector::FeaturePostProcessing>(self.getPostProcessing());
			},
			"Get the type of post-processing.")
		.def(
			"setPostProcessing",
			[](ArcEventFeatureDetector &self,
				const dv::features::ImageFeatureDetector::FeaturePostProcessing postProcessing) {
				self.setPostProcessing(static_cast<ArcEventFeatureDetector::FeaturePostProcessing>(postProcessing));
			},
			"postProcessing"_a, "Set the type of post-processing.")
		.def("getMargin", &ArcEventFeatureDetector::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", &ArcEventFeatureDetector::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", &ArcEventFeatureDetector::isWithinROI, py::arg("point"),
			"Check whether a point belongs to the ROI without the margins.")
		.def("getImageDimensions", &ArcEventFeatureDetector::getImageDimensions, "Get configured image dimensions.");
}
