#pragma once

#include "../../../include/dv-processing/kinematics/motion_compensator.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::kinematics::MotionCompensator<>::Info>(m, "MotionCompensatorInfo")
		.def_readwrite("imageCompensated", &dv::kinematics::MotionCompensator<>::Info::imageCompensated)
		.def_readwrite("depthAvailable", &dv::kinematics::MotionCompensator<>::Info::depthAvailable)
		.def_readwrite("transformsAvailable", &dv::kinematics::MotionCompensator<>::Info::transformsAvailable)
		.def_readwrite("depthTime", &dv::kinematics::MotionCompensator<>::Info::depthTime)
		.def_readwrite("generationTime", &dv::kinematics::MotionCompensator<>::Info::generationTime)
		.def_readwrite("inputEventCount", &dv::kinematics::MotionCompensator<>::Info::inputEventCount)
		.def_readwrite("accumulatedEventCount", &dv::kinematics::MotionCompensator<>::Info::accumulatedEventCount);

	py::class_<dv::kinematics::MotionCompensator<>>(m, "MotionCompensator")
		.def(py::init<std::shared_ptr<dv::camera::CameraGeometry>>(), py::arg("cameraGeometry"),
			"Construct a motion compensator instance with default accumulator. Default accumulator is a "
			"`dv::EdgeMapAccumulator` with default parameters.")
		.def(py::init([](const std::shared_ptr<dv::camera::CameraGeometry> &cameraGeometry,
						  const dv::EdgeMapAccumulator &acc) {
			return dv::kinematics::MotionCompensator<>(cameraGeometry, std::make_unique<dv::EdgeMapAccumulator>(acc));
		}))
		.def("getInfo", &dv::kinematics::MotionCompensator<>::getInfo,
			"Return an info class instance containing motion compensator state for the algorithm iteration. The info "
			"object contains debug information about the execution of the motion compensator.")
		.def("generateFrame", &dv::kinematics::MotionCompensator<>::generateFrame, py::arg("generationTime") = -1,
			"Generate the motion compensated frame output and reset the events contained in the buffer.")
		.def("generateEvents", &dv::kinematics::MotionCompensator<>::generateEvents, py::arg("generationTime") = -1,
			"Generate the motion compensated events contained in the buffer.")
		.def("reset", &dv::kinematics::MotionCompensator<>::reset, "Clear the event buffer.")
		.def(
			"accept",
			[](dv::kinematics::MotionCompensator<> &self, const dv::kinematics::Transformationf &transform) {
				self.accept(transform);
			},
			py::arg("transform"), "Push camera pose measurement.")
		.def(
			"accept",
			[](dv::kinematics::MotionCompensator<> &self, const dv::measurements::Depth &timeDepth) {
				self.accept(timeDepth);
			},
			py::arg("timeDepth"), "Scene depth measurement in meters.")
		.def(
			"accept",
			[](dv::kinematics::MotionCompensator<> &self, const dv::EventStore &events) {
				self.accept(events);
			},
			py::arg("events"), "Push event camera input.")
		.def(
			"accept",
			[](dv::kinematics::MotionCompensator<> &self, const dv::Event &event) {
				self.accept(event);
			},
			py::arg("event"), "Push event camera input.")
		.def("getConstantDepth", &dv::kinematics::MotionCompensator<>::getConstantDepth,
			"Get currently assumed constant depth value. It is used if no depth measurements are provided.")
		.def("setConstantDepth", &dv::kinematics::MotionCompensator<>::setConstantDepth, "depth"_a,
			"Set constant depth value that is assumed if no depth measurement is passed using "
			"`accept(dv::measurements::Depth)`. By default the constant depth is assumed to be 3.0 meters, which is "
			"just a reasonable guess.");
}
