#pragma once

#include "../../../include/dv-processing/depth/semi_dense_stereo_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_semi_dense_stereo_matcher(py::module &m) {
	using pybind11::operator""_a;

	py::class_<dv::SemiDenseStereoMatcher<>, std::shared_ptr<dv::SemiDenseStereoMatcher<>>>(m, "SemiDenseStereoMatcher",
		"Semi-dense stereo matcher - a class that performs disparity calculation using an OpenCV dense disparity "
		"calculation algorithm. The implementation performs accumulation of a stereo pair of images of input events "
		"and applies the semi-global block matching (SGBM) algorithm. ")
		.def(
			py::init([](const dv::EdgeMapAccumulator &leftAccumulator, const dv::EdgeMapAccumulator &rightAccumulator) {
				return dv::SemiDenseStereoMatcher<>(std::make_unique<dv::EdgeMapAccumulator>(leftAccumulator),
					std::make_unique<dv::EdgeMapAccumulator>(rightAccumulator), dv::depth::defaultStereoMatcher());
			}),
			"leftAccumulator"_a, "rightAccumulator"_a,
			"Construct a semi dense stereo matcher object by providing custom accumulators for left and right cameras."
			"The SGBM algorithm is initialized with minimum dispartiy = 0, number of disparities = 48, block size = "
			"11.")
		.def(py::init([](const cv::Size &leftResolution, const cv::Size &rightResolution) {
			return dv::SemiDenseStereoMatcher<>(leftResolution, rightResolution, dv::depth::defaultStereoMatcher());
		}),
			"leftResolution"_a, "rightResolution"_a,
			"Construct a semi dense stereo matcher object from resolution with default accumulators. "
			"The SGBM algorithm is initialized with minimum dispartiy = 0, number of disparities = 48, block size = "
			"11.")
		.def(py::init([](const dv::camera::StereoGeometry &stereoGeometry) {
			return dv::SemiDenseStereoMatcher<>(
				std::make_unique<dv::camera::StereoGeometry>(stereoGeometry), dv::depth::defaultStereoMatcher());
		}),
			"stereoGeometry"_a,
			"Construct a semi dense stereo matcher object using stereo geometry instance, enabling depth estimation. "
			"The SGBM algorithm is initialized with minimum dispartiy = 0, number of disparities = 48, block size = "
			"11.")
		.def(
			py::init([](const dv::camera::StereoGeometry &stereoGeometry, const dv::EdgeMapAccumulator &leftAccumulator,
						 const dv::EdgeMapAccumulator &rightAccumulator) {
				return dv::SemiDenseStereoMatcher<>(std::make_unique<dv::camera::StereoGeometry>(stereoGeometry),
					std::make_unique<dv::EdgeMapAccumulator>(leftAccumulator),
					std::make_unique<dv::EdgeMapAccumulator>(rightAccumulator), dv::depth::defaultStereoMatcher());
			}),
			"stereoGeometry"_a, "leftAccumulator"_a, "rightAccumulator"_a,
			"Construct a semi dense stereo matcher object using stereo geometry instance, enabling depth estimation "
			"and with custom left / right accumulators. The SGBM algorithm is initialized with minimum dispartiy = 0, "
			"number of disparities = 48, block size = 11.")
		.def("computeDisparity", &dv::SemiDenseStereoMatcher<>::computeDisparity, "leftEvents"_a, "rightEvents"_a,
			"Compute disparity of the two given event stores. The events will be accumulated using the accumulators "
			"for left and right camera accordingly and disparity is computed using the configured block matching "
			"algorithm. The function is not going to slice the input events, so event streams have to be synchronized "
			"and sliced accordingly. The `dv::StereoEventStreamSlicer` class is a good option for slicing stereo event "
			"streams. NOTE: Accumulated frames will be rectified only if a stereo geometry class was provided during "
			"construction.")
		.def("compute", &dv::SemiDenseStereoMatcher<>::compute, "leftImage"_a, "rightImage"_a,
			"Compute stereo disparity given a time synchronized pair of images. Images will be rectified before "
			"computing disparity if a StereoGeometry class instance was provided.")
		.def("getLeftFrame", &dv::SemiDenseStereoMatcher<>::getLeftFrame,
			"Retrieve the accumulated frame from the left camera event stream.")
		.def("getRightFrame", &dv::SemiDenseStereoMatcher<>::getRightFrame,
			"Retrieve the accumulated frame from the right camera event stream.")
		.def("estimateDepth", &dv::SemiDenseStereoMatcher<>::estimateDepth, "disparity"_a, "events"_a,
			"disparityScale"_a = 16.f,
			"Estimate depth given the disparity map and a list of events. The coordinates will be rectified and a "
			"disparity value will be looked up in the disparity map. The depth of each event is calculated using an "
			"equation: depth = (focalLength * baseline) / disparity. Focal length and disparity are expressed in meter "
			"distance. For practical applications, depth estimation should be evaluated prior to any use. The directly "
			"estimated depth values can contain measurable errors which should be accounted for - the errors can "
			"usually be within 10-20% fixed absolute error distance. Usually this comes from various inaccuracies and "
			"can be mitigated by introducing a correction factor for the depth estimate.")
		.def("estimateDepthFrame", &dv::SemiDenseStereoMatcher<>::estimateDepthFrame, "disparity"_a,
			"disparityScale"_a = 16.f,
			"Convert a disparity map into a depth frame. Each disparity value is converted into depth using the "
			"equation: depth = (focalLength * baseline) / disparity. Output frame contains distance "
			"values expressed in integer values of millimeter distance.");
}
