#pragma once

#include "../../../include/dv-processing/io/mono_camera_recording.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

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

	py::class_<dv::io::MonoCameraRecording, dv::io::InputBase>(m, "MonoCameraRecording",
		"A convenience class for reading recordings containing data captured from a single camera. Looks for an event, "
		"frame, imu, and trigger streams within the supplied aedat4 file.")
		.def(py::init<const std::filesystem::path &>(), py::arg("filePath"),
			"Create a reader that reads single camera data recording from an aedat4 file.")
		.def(py::init<const std::filesystem::path &, const std::string &>(), py::arg("filePath"),
			py::arg("cameraSerial"),
			"Create a reader that reads single camera data recording from an aedat4 file and tries to detect streams "
			"of a camera with given serial number.")
		.def(
			"getNextEventBatch",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getNextEventBatch(streamName);
			},
			"streamName"_a = "events",
			"Sequentially read a batch of recorded events. This function increments an internal seek counter which "
			"will return the next batch at each call.")
		.def(
			"getNextFrame",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getNextFrame(streamName);
			},
			"streamName"_a = "frames",
			"Sequential read of a frame. This function increments an internal seek counter which will return the next "
			"frame at each call.")
		.def(
			"getNextImuBatch",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getNextImuBatch(streamName);
			},
			"streamName"_a = "imu",
			"Sequentially read a batch of recorded imu data. This function increments an internal seek counter which "
			"will return the next batch at each call.")
		.def(
			"getNextTriggerBatch",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getNextTriggerBatch(streamName);
			},
			"streamName"_a = "triggers",
			"Sequentially read a batch of recorded triggers. This function increments an internal seek counter which "
			"will return the next batch at each call.")
		.def("getNextPose", &dv::io::MonoCameraRecording::getNextStreamPacket<dv::Pose>, "streamName"_a = "poses",
			"Sequential read of poses. This function increments an internal seek counter which will return the next "
			"packet at each call.")
		.def("getNextTimedKeyPoints", &dv::io::MonoCameraRecording::getNextStreamPacket<dv::TimedKeyPointPacket>,
			"streamName"_a = "timedKeyPoints",
			"Sequential read of dv::TimedKeyPointPacket. This function increments an internal seek counter which will "
			"return the next packet at each call.")
		.def("getNextBoundingBox", &dv::io::MonoCameraRecording::getNextStreamPacket<dv::BoundingBoxPacket>,
			"streamName"_a = "boundingBoxes",
			"Sequential read of dv::BoundingBoxPacket. This function increments an internal seek counter which will "
			"return the next packet at each call.")
		.def(
			"isEventStreamAvailable",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.isEventStreamAvailable(streamName);
			},
			"streamName"_a = "events", "Checks whether event data stream is present in the file.")
		.def(
			"isFrameStreamAvailable",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.isFrameStreamAvailable(streamName);
			},
			"streamName"_a = "frames", "Checks whether frame data stream is present in the file.")
		.def(
			"isImuStreamAvailable",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.isImuStreamAvailable(streamName);
			},
			"streamName"_a = "imu", "Checks whether imu data stream is present in the file.")
		.def(
			"isTriggerStreamAvailable",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.isTriggerStreamAvailable(streamName);
			},
			"streamName"_a = "triggers", "Checks whether trigger data stream is present in the file.")
		.def("getTimeRange", &dv::io::MonoCameraRecording::getTimeRange,
			"Return a pair containing start (first) and end (second) time of the recording file.")
		.def("getEventsTimeRange", &dv::io::MonoCameraRecording::getEventsTimeRange, "startTime"_a, "endTime"_a,
			"streamName"_a = "events", "Get events within given time range.")
		.def("getFramesTimeRange", &dv::io::MonoCameraRecording::getFramesTimeRange, "startTime"_a, "endTime"_a,
			"streamName"_a = "frames", "Get frames within given time range.")
		.def("getImuTimeRange", &dv::io::MonoCameraRecording::getImuTimeRange, "startTime"_a, "endTime"_a,
			"streamName"_a = "imu", "Get IMU data within given time range.")
		.def("getTriggersTimeRange", &dv::io::MonoCameraRecording::getTriggersTimeRange, "startTime"_a, "endTime"_a,
			"streamName"_a = "triggers", "Get trigger data within given time range.")
		.def("getPosesTimeRange", &dv::io::MonoCameraRecording::getStreamTimeRange<dv::Pose>, "startTime"_a,
			"endTime"_a, "streamName"_a = "poses", "Get poses within given time range.")
		.def("getTimedKeyPointsTimeRange", &dv::io::MonoCameraRecording::getStreamTimeRange<dv::TimedKeyPointPacket>,
			"startTime"_a, "endTime"_a, "streamName"_a = "timedKeyPoints",
			"Get timed key points within given time range.")
		.def("getBoundingBoxesTimeRange", &dv::io::MonoCameraRecording::getStreamTimeRange<dv::BoundingBoxPacket>,
			"startTime"_a, "endTime"_a, "streamName"_a = "boundingBoxes", "Get bounding boxes within given time range.")
		.def("resetSequentialRead", &dv::io::MonoCameraRecording::resetSequentialRead,
			"Reset the sequential read function to start from the beginning of the file.")
		.def(
			"getEventResolution",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getEventResolution(streamName);
			},
			"streamName"_a = "events", "Get the resolution of the event data stream if it is available.")
		.def(
			"getFrameResolution",
			[](dv::io::MonoCameraRecording &self, const std::string &streamName) {
				return self.getFrameResolution(streamName);
			},
			"streamName"_a = "frames", "Get the resolution of the frame data stream if it is available.")
		.def("isStreamOfEventType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::EventPacket>,
			"streamName"_a = "events", "Check whether a stream is of events type.")
		.def("isStreamOfFrameType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::Frame>,
			"streamName"_a = "frames", "Check whether a stream is of frame type.")
		.def("isStreamOfImuType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::IMUPacket>,
			"streamName"_a = "imu", "Check whether a stream is of imu type.")
		.def("isStreamOfTriggerType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::TriggerPacket>,
			"streamName"_a = "triggers", "Check whether a stream is of trigger type.")
		.def("isStreamOfPoseType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::Pose>, "streamName"_a = "poses",
			"Check whether a stream is of pose type.")
		.def("isStreamOfTimedKeyPointType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::TimedKeyPointPacket>,
			"streamName"_a = "timedKeyPoints", "Check whether a stream is of timed key point type.")
		.def("isStreamOfBoundingBoxType", &dv::io::MonoCameraRecording::isStreamOfDataType<dv::BoundingBoxPacket>,
			"streamName"_a = "boundingBoxes", "Check whether a stream is of bounding box type.")
		.def("getStreamNames", &dv::io::MonoCameraRecording::getStreamNames,
			"Return a vector containing all available stream names.")
		.def("getStreamMetadata", &dv::io::MonoCameraRecording::getStreamMetadata, "streamName"_a,
			"Get all metadata of a stream.")
		.def("getStreamMetadataValue", &dv::io::MonoCameraRecording::getStreamMetadataValue, "streamName"_a, "key"_a,
			"Get a value of a given metadata key. Throws an exception if given stream doesn't exist and returns "
			"None if a metadata entry with given key is not found for the stream.");
}
