#pragma once

#include "../../../../include/dv-processing/camera/calibrations/camera_calibration.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::camera::calibrations::CameraCalibration> cameraCalibration(m, "CameraCalibration");

	py::class_<dv::camera::calibrations::CameraCalibration::Metadata>(cameraCalibration, "Metadata")
		.def(py::init<const cv::Size &, const cv::Size &, const std::string &, float, float,
				 const std::optional<float> &, const std::string &, const std::string &, const std::string &,
				 const std::optional<float> &>(),
			py::arg("patternShape"), py::arg("internalPatternShape"), py::arg("patternType"), py::arg("patternSize"),
			py::arg("patternSpacing"), py::arg("calibrationError"), py::arg("calibrationTime"), py::arg("quality"),
			py::arg("comment"), py::arg("pixelPitch"))
		.def(py::init<>())
		.def_readonly("patternShape", &dv::camera::calibrations::CameraCalibration::Metadata::patternShape,
			"Shape of the calibration pattern")
		.def_readonly("internalPatternShape",
			&dv::camera::calibrations::CameraCalibration::Metadata::internalPatternShape,
			"Shape of the internal calibration pattern")
		.def_readonly("patternType", &dv::camera::calibrations::CameraCalibration::Metadata::patternType,
			"Type of the calibration pattern used (e.g. apriltag)")
		.def_readonly("patternSize", &dv::camera::calibrations::CameraCalibration::Metadata::patternSize,
			"Size of the calibration pattern in [m]")
		.def_readonly("patternSpacing", &dv::camera::calibrations::CameraCalibration::Metadata::patternSpacing,
			"Ratio between tags to patternSize (apriltag only)")
		.def_readonly("calibrationError", &dv::camera::calibrations::CameraCalibration::Metadata::calibrationError,
			"Calibration reprojection error")
		.def_readonly("calibrationTime", &dv::camera::calibrations::CameraCalibration::Metadata::calibrationTime,
			"Timestamp when the calibration was conducted")
		.def_readonly("quality", &dv::camera::calibrations::CameraCalibration::Metadata::quality,
			"Description of the calibration quality (excellent/good/bad etc)")
		.def_readonly(
			"comment", &dv::camera::calibrations::CameraCalibration::Metadata::comment, "Any additional information")
		.def_readonly(
			"pixelPitch", &dv::camera::calibrations::CameraCalibration::Metadata::pixelPitch, "Pixel pitch in meters");

	cameraCalibration.def(py::init<>())
		.def(py::init<const std::string &, const std::string &, const bool, const cv::Size &, const cv::Point2f &,
				 const cv::Point2f &, const std::vector<float> &, const dv::camera::DistortionModel &,
				 const dv::kinematics::Transformationf &,
				 const std::optional<dv::camera::calibrations::CameraCalibration::Metadata> &>(),
			py::arg("name"), py::arg("position"), py::arg("master"), py::arg("resolution"), py::arg("principalPoint"),
			py::arg("focalLength"), py::arg("distortion"), py::arg("distortionModel"), py::arg("transformationToC0"),
			py::arg("metadata"))
		.def_readonly(
			"name", &dv::camera::calibrations::CameraCalibration::name, "Camera name (e.g. 'DVXplorer_DXA02137')")
		.def_readonly(
			"resolution", &dv::camera::calibrations::CameraCalibration::resolution, "Camera sensor resolution")
		.def_readonly("master", &dv::camera::calibrations::CameraCalibration::master,
			"Indicate whether it is the master camera in a multi-camera rig")
		.def_readonly("focalLength", &dv::camera::calibrations::CameraCalibration::focalLength, "Focal length")
		.def_readonly("principalPoint", &dv::camera::calibrations::CameraCalibration::principalPoint,
			"Intersection of optical axis and image plane")
		.def_readonly("position", &dv::camera::calibrations::CameraCalibration::position,
			"Description of the location of the camera in the camera rig (e.g. 'left')")
		.def_readonly("transformationToC0", &dv::camera::calibrations::CameraCalibration::transformationToC0,
			"Kinematics transformation from current camera to initial/reference coordinate frame")
		.def_readonly("distortion", &dv::camera::calibrations::CameraCalibration::distortion, "Distortion coefficients")
		.def_readonly("distortionModel", &dv::camera::calibrations::CameraCalibration::distortionModel,
			"Distortion model used (e.g. radialTangential)")
		.def_readonly("metadata", &dv::camera::calibrations::CameraCalibration::metadata, "Metadata")
		.def("getCameraGeometry", &dv::camera::calibrations::CameraCalibration::getCameraGeometry,
			"Retrieve camera geometry instance from this calibration instance. Distortion model is going to be ignored "
			"if the `CameraGeometry` class doesn't support the distortion model.")
		.def(
			"getCameraMatrix",
			[](const dv::camera::calibrations::CameraCalibration &self) -> Eigen::Matrix3f {
				Eigen::Matrix3f mat;
				cv::cv2eigen(self.getCameraMatrix(), mat);
				return mat;
			},
			"Get camera matrix in the format: | mFx 0 mCx | | 0 mFy mCy | | 0 0 1 |");
}
