#pragma once

#include "../../../include/dv-processing/data/landmark_base.hpp"

#include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>

PYBIND11_MAKE_OPAQUE(std::vector<dv::Landmark>)

namespace py = pybind11;

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

	py::class_<dv::Observation>(m, "Observation")
		.def(py::init<>())
		.def(py::init<int32_t, int32_t, const std::string &, int64_t>(), "trackId"_a, "cameraId"_a, "cameraName"_a,
			"timestamp"_a)
		.def_readwrite(
			"trackId", &dv::Observation::trackId, "The tracking sequence ID that the landmark is observed by a camera")
		.def_readwrite(
			"cameraId", &dv::Observation::cameraId, "Arbitrary ID of the camera, this can be application specific")
		.def_readwrite("cameraName", &dv::Observation::cameraName, "Name of the camera. Optional.")
		.def_readwrite("timestamp", &dv::Observation::timestamp, "Timestamp of the observation (µs).");
}

/**
 * @brief Bind Landmark and dv::LandmarkPacket class to python
 * @param m pybind The module to bind to
 */
void bind_landmark(py::module &m) {
	using pybind11::operator""_a;

	// Bind dv::Landmark
	py::class_<dv::Landmark>(m, "Landmark")
		.def(py::init<>())
		.def(py::init<const dv::Point3f &, int64_t, int64_t, const std::vector<int8_t> &, const std::string &,
				 const std::vector<float> &, const std::vector<dv::Observation> &>(),
			"point"_a, "id"_a, "timestamp"_a, "descriptor"_a, "descriptorType"_a, "covariance"_a, "observations"_a)
		.def_readwrite("pt", &dv::Landmark::pt, "3D coordinate of the landmark.")
		.def_readwrite(
			"id", &dv::Landmark::id, "Landmark id (if the keypoints need to be clustered by an object they belong to).")
		.def_readwrite("timestamp", &dv::Landmark::timestamp, "Timestamp (µs).")
		.def_readwrite("descriptor", &dv::Landmark::descriptor, "Visual descriptor of the landmark.")
		.def_readwrite("descriptorType", &dv::Landmark::descriptorType, "Type of the visual descriptor.")
		.def_readwrite("covariance", &dv::Landmark::covariance,
			"Covariance matrix, must contain 9 numbers. It is represented as a 3x3 square matrix.")
		.def_readwrite("observations", &dv::Landmark::observations,
			"Observation info, can be from multiple cameras if they are matched using descriptor.");

	// Bind LandmarksPacket
	py::class_<dv::LandmarksPacket> landmarksPacketClass(m, "LandmarksPacket");
	py::bind_vector<std::vector<dv::Landmark>>(landmarksPacketClass, "LandmarksVector");

	landmarksPacketClass.def(py::init<>())
		.def(py::init<const std::vector<dv::Landmark> &, const std::string &>(), "elements"_a, "referenceFrame"_a,
			"Create a landmarks packet from a list of landmarks")
		.def_readwrite("elements", &dv::LandmarksPacket::elements, "3D coordinate of the landmark.")
		.def_readwrite("referenceFrame", &dv::LandmarksPacket::referenceFrame,
			"Landmark id (if the keypoints need to be clustered by an object they belong to).")
		.def_static("GetFullyQualifiedName", &dv::LandmarksPacket::GetFullyQualifiedName)
		.def_static("PacketIdentifier", &dv::LandmarksPacketIdentifier);

	py::implicitly_convertible<std::vector<dv::Landmark>, dv::LandmarksPacket>();
}

/**
 * @brief Bind all landmark related classes to python
 * @param m pybind The module to bind to
 */
void bind_landmark_base(py::module &m) {
	bind_observation(m);
	bind_landmark(m);
}
