#pragma once

#include "../../../include/dv-processing/depth/sparse_event_block_matcher.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

/**
 * @brief Bind SemiDenseStereoMatcher class to python
 * @param m pybind The module to bind to
 */
void bind_sparse_event_block_matcher(py::module &m) {
	using pybind11::operator""_a;

	py::class_<dv::SparseEventBlockMatcher, std::shared_ptr<dv::SparseEventBlockMatcher>> m_sparseMatcher(
		m, "SparseEventBlockMatcher");

	py::class_<dv::SparseEventBlockMatcher::PixelDisparity>(m_sparseMatcher, "PixelDisparity")
		.def(py::init<const cv::Point2i &, const bool, const std::optional<float>, const std::optional<float>,
				 const std::optional<int32_t>>(),
			"coordinates"_a, "valid"_a, "correlation"_a = std::nullopt, "score"_a = std::nullopt,
			"disparity"_a = std::nullopt)
		.def_readwrite("coordinates", &dv::SparseEventBlockMatcher::PixelDisparity::coordinates)
		.def_readwrite("valid", &dv::SparseEventBlockMatcher::PixelDisparity::valid)
		.def_readwrite("correlation", &dv::SparseEventBlockMatcher::PixelDisparity::correlation)
		.def_readwrite("score", &dv::SparseEventBlockMatcher::PixelDisparity::score)
		.def_readwrite("disparity", &dv::SparseEventBlockMatcher::PixelDisparity::disparity)
		.def_readwrite("templatePosition", &dv::SparseEventBlockMatcher::PixelDisparity::templatePosition)
		.def_readwrite("matchedPosition", &dv::SparseEventBlockMatcher::PixelDisparity::matchedPosition);

	m_sparseMatcher
		.def(py::init([](const dv::camera::StereoGeometry &stereoGeometry, const cv::Size &windowSize,
						  const int32_t maxDisparity, const int32_t minDisparity, const float minScore) {
			return dv::SparseEventBlockMatcher(std::make_unique<dv::camera::StereoGeometry>(stereoGeometry), windowSize,
				maxDisparity, minDisparity, minScore);
		}),
			"geometry"_a, "windowSize"_a = cv::Size(24, 24), "maxDisparity"_a = 40, "minDisparity"_a = 0,
			"minScore"_a = 1.0f,
			"Initialize a sparse stereo block matcher with a calibrated stereo geometry. This allows event "
			"rectification "
			"while calculating the disparity.")
		.def(py::init<const cv::Size &, const cv::Size &, const int32_t, const int32_t, const float>(), "resolution"_a,
			"windowSize"_a = cv::Size(24, 24), "maxDisparity"_a = 40, "minDisparity"_a = 0, "minScore"_a = 1.0f,
			"Initialize sparse event block matcher. This constructor initializes the matcher in non-rectified space, "
			"so for accurate results the event coordinates should be already rectified.")
		.def("computeDisparitySparse",
			&dv::SparseEventBlockMatcher::computeDisparitySparse<std::vector<Eigen::Vector2i>>, "left"_a, "right"_a,
			"interestPoints"_a,
			"Compute sparse disparity on given interest points. The events are accumulated sparsely only on the "
			"selected interest point regions. Returns a list of coordinates with their according disparity values, "
			"correlations and scores for each disparity match. If rectification is enabled, the returned disparity "
			"result will have `valid` flag set to false if the interest point coordinate lies outside of valid "
			"rectified pixel space. Input event has to be passed in synchronized batches, no time validation is "
			"performed during accumulation.")
		.def("getLeftMask", &dv::SparseEventBlockMatcher::getLeftMask,
			"Get the left camera image mask. The algorithm only accumulates the frames where actual matching is going "
			"to happen. The mask will contain non-zero pixel values where accumulation needs to happen.")
		.def("getRightMask", &dv::SparseEventBlockMatcher::getRightMask,
			"Get the right camera image mask. The algorithm only accumulates the frames where actual matching is going "
			"to happen. The mask will contain non-zero pixel values where accumulation needs to happen.")
		.def("getLeftFrame", &dv::SparseEventBlockMatcher::getLeftFrame, "Get the latest accumulated left frame.")
		.def("getRightFrame", &dv::SparseEventBlockMatcher::getRightFrame, "Get the latest accumulated right frame.")
		.def("getWindowSize", &dv::SparseEventBlockMatcher::getWindowSize, "Get matching window size.")
		.def("setWindowSize", &dv::SparseEventBlockMatcher::setWindowSize, "windowSize"_a,
			"Set matching window size. This is the size of cropped template image that is matched along the epipolar "
			"line of the stereo geometry.")
		.def("getMaxDisparity", &dv::SparseEventBlockMatcher::getMaxDisparity, "Get maximum disparity value.")
		.def("setMaxDisparity", &dv::SparseEventBlockMatcher::setMaxDisparity, "maxDisparity"_a,
			"Set maximum measured disparity. This parameter limits the matching space in pixels on the right camera "
			"image.")
		.def("getMinDisparity", &dv::SparseEventBlockMatcher::getMinDisparity, "Get minimum disparity value.")
		.def("setMinDisparity", &dv::SparseEventBlockMatcher::setMinDisparity, "minDisparity"_a,
			"Set minimum measured disparity. This parameter limits the matching space in pixels on the right camera "
			"image.")
		.def("getMinScore", &dv::SparseEventBlockMatcher::getMinScore, "Get minimum matching score value.")
		.def("setMinScore", &dv::SparseEventBlockMatcher::setMinScore, "minimumScore"_a,
			"Set minimum matching score value to consider the matching valid. If matching score is below this "
			"threshold, the value for a point will be set to an invalid value and `valid` boolean to false. Score is "
			"calculated by applying softmax function on the discrete distribution of correlation values from matching "
			"the template left patch on the epipolar line of the right camera image. This retrieves the probability "
			"mass function of the correlations. The best match is found by finding the max probability value and score "
			"is calculated for the best match by computing z-score over the probabilities.");
}
