#pragma once

#include "../../../include/dv-processing/camera/stereo_geometry.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::camera::StereoGeometry, std::shared_ptr<dv::camera::StereoGeometry>> stereoGeometryClass(
		m, "StereoGeometry");

	py::enum_<dv::camera::StereoGeometry::CameraPosition>(stereoGeometryClass, "CameraPosition")
		.value("LEFT", dv::camera::StereoGeometry::CameraPosition::LEFT)
		.value("RIGHT", dv::camera::StereoGeometry::CameraPosition::RIGHT);

	stereoGeometryClass
		.def(py::init<const dv::camera::CameraGeometry &, const dv::camera::CameraGeometry &,
				 const dv::kinematics::Transformationf &, std::optional<cv::Size>>(),
			"leftCamera"_a, "rightCamera"_a, "transformToLeft"_a, "rectifiedResolution"_a = std::nullopt)
		.def(py::init<const dv::camera::calibrations::CameraCalibration &,
				 const dv::camera::calibrations::CameraCalibration &>(),
			"leftCalibration"_a, "rightCalibration"_a)
		.def("remapImage", &dv::camera::StereoGeometry::remapImage, "cameraPosition"_a, "image"_a,
			"Apply remapping to an input image to rectify it.")
		.def("remapEvents", &dv::camera::StereoGeometry::remapEvents, "cameraPosition"_a, "events"_a,
			"Apply remapping of input events coordinates into undistorted and rectified pixel space.")
		.def("remapPoint", &dv::camera::StereoGeometry::remapPoint<Eigen::Vector2i, Eigen::Vector2i>,
			"cameraPosition"_a, "point"_a,
			"Apply remapping on a single coordinate into undistorted and rectified pixel space.")
		.def(
			"unmapPoint",
			[](const dv::camera::StereoGeometry &self, const dv::camera::StereoGeometry::CameraPosition position,
				const Eigen::Vector2f &point, const bool subPixel) {
				if (subPixel) {
					return self
						.unmapPoint<Eigen::Vector2f, dv::camera::StereoGeometry::FunctionImplementation::SUB_PIXEL>(
							position, point);
				}
				else {
					return self.unmapPoint<Eigen::Vector2f>(position, point);
				}
			},
			"cameraPosition"_a, "point"_a, "subPixel"_a = false,
			"Unmap a point coordinate from undistorted and rectified pixel space into original distorted pixel.")
		.def("getLeftCameraGeometry", &dv::camera::StereoGeometry::getLeftCameraGeometry,
			"Retrieve left camera geometry class that can project coordinates into stereo rectified space.")
		.def("getRightCameraGeometry", &dv::camera::StereoGeometry::getRightCameraGeometry,
			"Retrieve right camera geometry class that can project coordinates into stereo rectified space.")
		.def("estimateDepth", &dv::camera::StereoGeometry::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 "
			"pixels. 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("convertDisparityToDepth", &dv::camera::StereoGeometry::convertDisparityToDepth, "disparity"_a,
			"Convert a disparity value (in rectified coordinates) [px] to a depth value [mm] using the mapping depth = "
			"(focalLength * baseline) / disparity.")
		.def("toDepthFrame", &dv::camera::StereoGeometry::toDepthFrame, "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.");
}
