#pragma once

#include "../../../include/dv-processing/visualization/pose_visualizer.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::visualization::PoseVisualizer> poseVisualizerClass(m, "PoseVisualizer");

	py::enum_<dv::visualization::PoseVisualizer::ViewMode>(poseVisualizerClass, "ViewMode")
		.value("CUSTOM", dv::visualization::PoseVisualizer::ViewMode::CUSTOM)
		.value("VIEW_XY", dv::visualization::PoseVisualizer::ViewMode::VIEW_XY)
		.value("VIEW_YZ", dv::visualization::PoseVisualizer::ViewMode::VIEW_YZ)
		.value("VIEW_ZX", dv::visualization::PoseVisualizer::ViewMode::VIEW_ZX)
		.value("VIEW_XZ", dv::visualization::PoseVisualizer::ViewMode::VIEW_XZ)
		.value("VIEW_YX", dv::visualization::PoseVisualizer::ViewMode::VIEW_YX)
		.value("VIEW_ZY", dv::visualization::PoseVisualizer::ViewMode::VIEW_ZY);

	py::enum_<dv::visualization::PoseVisualizer::GridPlane>(poseVisualizerClass, "GridPlane")
		.value("PLANE_NONE", dv::visualization::PoseVisualizer::GridPlane::PLANE_NONE)
		.value("PLANE_XY", dv::visualization::PoseVisualizer::GridPlane::PLANE_XY)
		.value("PLANE_YZ", dv::visualization::PoseVisualizer::GridPlane::PLANE_YZ)
		.value("PLANE_ZX", dv::visualization::PoseVisualizer::GridPlane::PLANE_ZX);

	poseVisualizerClass
		.def(py::init<const cv::Size &, const cv::Scalar &, const cv::Scalar &, const float, const size_t,
				 const size_t>(),
			"resolution"_a = cv::Size(640, 480), "backgroundColor"_a = dv::visualization::colors::darkGray,
			"gridColor"_a = dv::visualization::colors::gray, "gridSpacing"_a = 1.0f, "trajectoryLength"_a = 10'000,
			"landmarkLimit"_a = 10'000)
		.def(py::init<const float, const float, const float, const float, const cv::Size &, const cv::Scalar &,
				 const cv::Scalar &, const float, const size_t, const size_t>(),
			"fx"_a, "fy"_a, "cx"_a, "cy"_a, "resolution"_a, "backgroundColor"_a = dv::visualization::colors::darkGray,
			"gridColor"_a = dv::visualization::colors::gray, "gridSpacing"_a = 1.0f, "trajectoryLength"_a = 10'000,
			"landmarkLimit"_a = 10'000)
		.def(py::init<const dv::camera::CameraGeometry &, const cv::Scalar &, const cv::Scalar &, const float,
				 const size_t, const size_t>(),
			"cameraGeometry"_a, "backgroundColor"_a = dv::visualization::colors::darkGray,
			"gridColor"_a = dv::visualization::colors::gray, "gridSpacing"_a = 1.0f, "trajectoryLength"_a = 10'000,
			"landmarkLimit"_a = 10'000)
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const std::string &objectName, const dv::Pose &objectPose) {
				self.accept(objectName, objectPose);
			},
			"objectName"_a, "objectPose"_a, "Add a new object pose to the visualization")
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const std::string &objectName,
				const dv::kinematics::Transformationf &objectPose) {
				self.accept(objectName, objectPose);
			},
			"objectName"_a, "objectPose"_a, "Add a new object pose to the visualization")
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const dv::Pose &objectPose) {
				self.accept(objectPose);
			},
			"objectPose"_a, "Add a new object pose to the visualization")
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const dv::kinematics::Transformationf &objectPose) {
				self.accept(objectPose);
			},
			"objectPose"_a, "Add a new object pose to the visualization")
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const dv::LandmarksPacket &landmarks) {
				self.accept(landmarks);
			},
			"landmarks"_a, "Add landmarks to the visualization.")
		.def(
			"accept",
			[](dv::visualization::PoseVisualizer &self, const dv::Landmark &landmark) {
				self.accept(landmark);
			},
			"landmark"_a, "Add a landmark to the visualization.")
		.def("generateFrame", &dv::visualization::PoseVisualizer::generateFrame,
			"Return a visualization image of the 3D scene from the defined virtual camera perspective.")
		.def("getTimestamp", &dv::visualization::PoseVisualizer::getTimestamp,
			"Return the timestamp of the most recent object pose.")
		.def("clearLandmarkWithId", &dv::visualization::PoseVisualizer::clearLandmarkWithId,
			"id"_a
			"Clear landmark with given ID from the visualization.")
		.def("clearLandmarksInInterval", &dv::visualization::PoseVisualizer::clearLandmarksInInterval, "startTime"_a,
			"endTime"_a, "Clear all landmarks from the visualization within the time interval [startTime, endTime).")
		.def("clearLandmarks", &dv::visualization::PoseVisualizer::clearLandmarks,
			"Clear all landmarks from the visualization.")
		.def("clearObject", &dv::visualization::PoseVisualizer::clearObject, "objectName"_a,
			"Clear all saved poses for the given object from the visualization.")
		.def("clearObjectPosesInInterval", &dv::visualization::PoseVisualizer::clearObjectPosesInInterval,
			"startTime"_a, "endTime"_a,
			"Clear all saved object poses from the visualization with the time interval [startTime, endTime).")
		.def("clearAllObjects", &dv::visualization::PoseVisualizer::clearAllObjects,
			"Clear all saved object poses from the visualization.")
		.def("reset", &dv::visualization::PoseVisualizer::reset,
			"Reset the visualizer to a default state (camera position, resolution, etc.).")

		.def("getResolution", &dv::visualization::PoseVisualizer::getResolution,
			"Get the resolution of the virtual camera used for visualization.")
		.def("setResolution", &dv::visualization::PoseVisualizer::setResolution,
			"resolution"_a
			"Set the resolution of the virtual camera used for visualization.")
		.def("getCameraIntrinsics", &dv::visualization::PoseVisualizer::getCameraIntrinsics,
			"Get the intrinsics of the virtual camera used for visualization.")
		.def(
			"setCameraIntrinsics",
			[](dv::visualization::PoseVisualizer &self, const dv::camera::CameraGeometry &cameraGeometry) {
				self.setCameraIntrinsics(cameraGeometry);
			},
			"cameraGeometry"_a, "Set the intrinsics of the virtual camera used for visualization.")
		.def(
			"setCameraIntrinsics",
			[](dv::visualization::PoseVisualizer &self, const float fx, const float fy, const float cx, const float cy,
				const cv::Size &resolution) {
				self.setCameraIntrinsics(fx, fy, cx, cy, resolution);
			},
			"fx"_a, "fy"_a, "cx"_a, "cy"_a, "resolution"_a,
			"Set the intrinsics of the virtual camera used for visualization.")

		.def("getCameraPosition", &dv::visualization::PoseVisualizer::getCameraPosition<Eigen::Vector3f>,
			"Get the position of the virtual camera used for visualization with respect to the world coordinate "
			"system.")
		.def("setCameraPosition", &dv::visualization::PoseVisualizer::setCameraPosition<Eigen::Vector3f>,
			"newPosition"_a,
			"Set the position of the virtual camera used for visualization with respect to the world coordinate "
			"system.")
		.def("getCameraOrientation", &dv::visualization::PoseVisualizer::getCameraOrientation<Eigen::Vector3f>,
			"Get the orientation of the virtual camera used for visualization as XYZ Euler angles (in degrees) with "
			"respect to the world coordinate system.")
		.def("setCameraOrientation", &dv::visualization::PoseVisualizer::setCameraOrientation<Eigen::Vector3f>,
			"newPosition"_a,
			"Set the orientation of the virtual camera used for visualization as XYZ Euler angles (in degrees) with "
			"respect to the world coordinate system.")
		.def("getCameraPose", &dv::visualization::PoseVisualizer::getCameraPose,
			"Get the pose of the virtual camera used for visualization with respect to the world coordinate system.")
		.def("setCameraPose", &dv::visualization::PoseVisualizer::setCameraPose, "T_W_C"_a,
			"Set the pose of the virtual camera used for visualization with respect to the world coordinate system.")
		.def("getViewMode", &dv::visualization::PoseVisualizer::getViewMode,
			"Get the view mode (pre-defined pose) of the virtual camera used for visualization.")
		.def("setViewMode", &dv::visualization::PoseVisualizer::setViewMode, "mode"_a,
			"Set the view mode (pre-defined pose) of the virtual camera used for visualization to one of the "
			"pre-defined view modes.")
		.def("getGridPlane", &dv::visualization::PoseVisualizer::getGridPlane,
			"Get the grid plane orientation with respect to the world coordinate system (from a list of pre-defined "
			"orientations).")
		.def("setGridPlane", &dv::visualization::PoseVisualizer::setGridPlane, "gridPlane"_a,
			"Set the grid plane orientation with respect to the world coordinate system (from a list of pre-defined "
			"orientations). If set to PLANE_NONE, the grid plane is removed from the visualization.")
		.def("getGridSpacing", &dv::visualization::PoseVisualizer::getGridSpacing,
			"Get the spacing between drawn lines used for visualizing the grid plane, in physical units [m].")
		.def("setGridSpacing", &dv::visualization::PoseVisualizer::setGridSpacing, "gridSpacing"_a,
			"Set the spacing between drawn lines used for visualizing the grid plane, in physical units [m].")
		.def("getWorldCoordinateSize", &dv::visualization::PoseVisualizer::getWorldCoordinateSize,
			"Get the size of the visualized world coordinate frame, in physical units [m].")
		.def("setWorldCoordinateSize", &dv::visualization::PoseVisualizer::setWorldCoordinateSize, "newSize"_a,
			"Set the size of the visualized world coordinate frame, in physical units [m]. To disable the "
			"visualization of the world coordinate frame, set this value to a value <=0.")
		.def("getObjectCoordinateSize", &dv::visualization::PoseVisualizer::getObjectCoordinateSize,
			"Get the size of the visualized coordinate frame for object poses, in physical units [m].")
		.def("setObjectCoordinateSize", &dv::visualization::PoseVisualizer::setObjectCoordinateSize, "newSize"_a,
			"Set the size of the visualized for object poses, in physical units [m]. To disable the "
			"Set the size of the visualized for object poses, in physical units [m]. To disable the "
			"visualization of the coordinate frame, set this value to a value <=0.")

		.def("getLineThickness", &dv::visualization::PoseVisualizer::getLineThickness,
			"Get the line thickness used for drawing, in pixels.")
		.def("setLineThickness", &dv::visualization::PoseVisualizer::setLineThickness, "newThickness"_a,
			"Set the line thickness used for drawing, in pixels.")
		.def("getBackgroundColor", &dv::visualization::PoseVisualizer::getBackgroundColor, "Get the background color.")
		.def("setBackgroundColor", &dv::visualization::PoseVisualizer::setBackgroundColor, py::arg("color"),
			"Set new background color.")
		.def("getGridColor", &dv::visualization::PoseVisualizer::getGridColor, "Get the grid line color.")
		.def("setGridColor", &dv::visualization::PoseVisualizer::setGridColor, py::arg("color"),
			"Set new grid line color")
		.def("getLandmarkLimit", &dv::visualization::PoseVisualizer::getLandmarkLimit,
			"Get the maximum number of landmarks to be drawn.")
		.def("setLandmarkLimit", &dv::visualization::PoseVisualizer::setLandmarkLimit, "landmarkLimit"_a,
			"Set a limit for number of landmarks that are stored and drawn.")
		.def("getNumberOfLandmarks", &dv::visualization::PoseVisualizer::getNumberOfLandmarks,
			"Get the number of landmarks currently stored in the visualizer.")
		.def("getNumberOfObjects", &dv::visualization::PoseVisualizer::getNumberOfObjects,
			"Get the number of objects who's poses are currently stored in the visualizer.")
		.def("isAutoScalingEnabled", &dv::visualization::PoseVisualizer::isAutoScalingEnabled,
			"Get if auto-scaling of the visualizer is enabled; scaling of the grid and viewing camera position based "
			"on the minimum and maximum bounds from the provided data.")
		.def("setAutoScaling", &dv::visualization::PoseVisualizer::setAutoScaling,
			" Enable/disable auto-scaling of the visualizer; scaling of the grid and viewing camera position based on "
			"the minimum and maximum bounds from the provided data.")
		.def("isLegendVisualizationEnabled", &dv::visualization::PoseVisualizer::isLegendVisualizationEnabled,
			"Get if the visualization of the legend is enabled.")
		.def("setLegendVisualization", &dv::visualization::PoseVisualizer::setLegendVisualization,
			"Enable the visualization of a legend showing the viewing camera position, latest timestamp of provided "
			"data, and the spacing of the drawn grid.");
}
