#pragma once

#include "../../../include/dv-processing/io/camera/dvxplorer_m.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

/**
 * @brief Bind discoverDevices class to python
 * @param m pybind The module to bind to
 */
void bind_dvxplorer_m(py::module &m) {
	static dv::io::camera::USBDevice::loggerCallbackType DEFAULT_CLI_LOGGER
		= [](const dv::io::camera::USBDevice::LogLevel level, const std::string_view deviceName,
			  const std::string_view message) {
			  std::cerr << fmt::format("[{}] {}: {}", level, deviceName, message) << std::endl;
		  };

	py::class_<dv::io::camera::DVXplorerM, dv::io::camera::USBDeviceNextGen, dv::io::camera::CameraInputBase>
		dvxplorer_m(m, "DVXplorerM", "DVXplorerM camera.");

	dvxplorer_m.def_static(
		"findDevices",
		[](const std::string_view filterBySerialNumber) {
			return dv::io::camera::DVXplorerM::findDevices(filterBySerialNumber, DEFAULT_CLI_LOGGER);
		},
		py::arg("filterBySerialNumber") = "", "Find connected DVXplorerM cameras.");

	// Events.
	dvxplorer_m
		.def(py::init([](const dv::io::camera::USBDevice::LogLevel deviceLogLevel) {
			return std::make_unique<dv::io::camera::DVXplorerM>(DEFAULT_CLI_LOGGER, deviceLogLevel);
		}),
			py::arg("deviceLogLevel") = dv::io::camera::USBDevice::LogLevel::LVL_WARNING,
			"Open the first DVXplorerM camera that can be found.")
		.def(py::init([](const std::string_view filterBySerialNumber,
						  const dv::io::camera::USBDevice::LogLevel deviceLogLevel) {
			return std::make_unique<dv::io::camera::DVXplorerM>(
				filterBySerialNumber, DEFAULT_CLI_LOGGER, deviceLogLevel);
		}),
			py::arg("filterBySerialNumber"),
			py::arg("deviceLogLevel") = dv::io::camera::USBDevice::LogLevel::LVL_WARNING,
			"Open the DVXplorerM camera with the specified serial number.")
		.def(py::init([](const dv::io::camera::USBDevice::DeviceDescriptor &deviceToOpen,
						  const dv::io::camera::USBDevice::LogLevel deviceLogLevel) {
			return std::make_unique<dv::io::camera::DVXplorerM>(deviceToOpen, DEFAULT_CLI_LOGGER, deviceLogLevel);
		}),
			py::arg("deviceToOpen"), py::arg("deviceLogLevel") = dv::io::camera::USBDevice::LogLevel::LVL_WARNING,
			"Open the DVXplorerM camera corresponding to the specified descriptor.")
		.def("configLoadFromFlash", &dv::io::camera::DVXplorerM::configLoadFromFlash,
			"Load configuration from on-device flash memory.")
		.def("configStoreToFlash", &dv::io::camera::DVXplorerM::configStoreToFlash,
			"Store current configuration to on-device flash memory.")
		.def("getSubSampleHorizontal", &dv::io::camera::DVXplorerM::getSubSampleHorizontal,
			"Get horizontal subsampling mode.")
		.def("setSubSampleHorizontal", &dv::io::camera::DVXplorerM::setSubSampleHorizontal,
			py::arg("subSampleHorizontal"), "Set horizontal subsampling mode.")
		.def(
			"getSubSampleVertical", &dv::io::camera::DVXplorerM::getSubSampleVertical, "Get vertical subsampling mode.")
		.def("setSubSampleVertical", &dv::io::camera::DVXplorerM::setSubSampleVertical, py::arg("subSampleVertical"),
			"Set vertical subsampling mode.")
		.def("getGlobalHold", &dv::io::camera::DVXplorerM::getGlobalHold, "Report status of global hold feature.")
		.def("setGlobalHold", &dv::io::camera::DVXplorerM::setGlobalHold, py::arg("globalHold"),
			"Enable or disable global hold feature. Default is enabled.")
		.def("getGlobalReset", &dv::io::camera::DVXplorerM::getGlobalReset, "Report status of global reset feature.")
		.def("setGlobalReset", &dv::io::camera::DVXplorerM::setGlobalReset, py::arg("globalReset"),
			"Enable or disable global reset feature.")
		.def("getGlobalResetSkip", &dv::io::camera::DVXplorerM::getGlobalResetSkip, "Get global reset skip value.")
		.def("setGlobalResetSkip", &dv::io::camera::DVXplorerM::setGlobalResetSkip, py::arg("globalResetSkip"),
			"Control global reset skip feature.")
		.def("getMIPITimeoutEnable", &dv::io::camera::DVXplorerM::getMIPITimeoutEnable,
			"Report status of MIPI timeout feature.")
		.def("setMIPITimeoutEnable", &dv::io::camera::DVXplorerM::setMIPITimeoutEnable, py::arg("mipiTimeoutEnable"),
			"Enable MIPI timeout, will ensure data is sent from sensor to host at least every N microseconds, even "
			"during periods of low activity.")
		.def("getMIPITimeoutValue", &dv::io::camera::DVXplorerM::getMIPITimeoutValue,
			"Get MIPI timeout in microseconds.")
		.def("setMIPITimeoutValue", &dv::io::camera::DVXplorerM::setMIPITimeoutValue, py::arg("mipiTimeoutValueUs"),
			"Set MIPI timeout in microseconds.")
		.def("getReadoutFPS", &dv::io::camera::DVXplorerM::getReadoutFPS,
			"Get currently set event-frame readout frequency.")
		.def("setReadoutFPS", &dv::io::camera::DVXplorerM::setReadoutFPS, py::arg("readoutFps"),
			"Set frequency of event-frame readouts on sensor.")
		.def("getContrastThresholdOn", &dv::io::camera::DVXplorerM::getContrastThresholdOn,
			"Get the contrast threshold for ON polarity event generation.")
		.def("setContrastThresholdOn", &dv::io::camera::DVXplorerM::setContrastThresholdOn,
			py::arg("contrastThresholdOn"), "Set the contrast threshold for ON polarity event generation.")
		.def("getContrastThresholdOff", &dv::io::camera::DVXplorerM::getContrastThresholdOff,
			"Get the contrast threshold for OFF polarity event generation.")
		.def("setContrastThresholdOff", &dv::io::camera::DVXplorerM::setContrastThresholdOff,
			py::arg("contrastThresholdOff"), "Set the contrast threshold for OFF polarity event generation.")
		.def("getAreaBlock", &dv::io::camera::DVXplorerM::getAreaBlock, py::arg("blockX"), py::arg("blockY"),
			"Report if the pixel block at the given X,Y address is filtered out or not.")
		.def("setAreaBlock", &dv::io::camera::DVXplorerM::setAreaBlock, py::arg("blockX"), py::arg("blockY"),
			py::arg("block"), "Enable pixel block filtering at the given X,Y address.");

	// IMU.
	dvxplorer_m
		.def("isImuRunningAccelerometer", &dv::io::camera::DVXplorerM::isImuRunningAccelerometer,
			"Status of IMU accelerometer.")
		.def("setImuRunningAccelerometer", &dv::io::camera::DVXplorerM::setImuRunningAccelerometer, py::arg("run"),
			"Enable or disable IMU accelerometer.")
		.def("isImuRunningGyroscope", &dv::io::camera::DVXplorerM::isImuRunningGyroscope, "Status of IMU gyroscope.")
		.def("setImuRunningGyroscope", &dv::io::camera::DVXplorerM::setImuRunningGyroscope, py::arg("run"),
			"Enable or disable IMU gyroscope.")
		.def("isImuRunningTemperature", &dv::io::camera::DVXplorerM::isImuRunningTemperature,
			"Status of IMU temperature measurement.")
		.def("setImuRunningTemperature", &dv::io::camera::DVXplorerM::setImuRunningTemperature, py::arg("run"),
			"Enable or disable IMU temperature measurement.")
		.def("getImuAccelDataRate", &dv::io::camera::DVXplorerM::getImuAccelDataRate,
			"Get current IMU accelerometer data rate.")
		.def("setImuAccelDataRate", &dv::io::camera::DVXplorerM::setImuAccelDataRate, py::arg("dataRate"),
			"Set IMU accelerometer data rate.")
		.def("getImuAccelFilter", &dv::io::camera::DVXplorerM::getImuAccelFilter,
			"Get current IMU accelerometer filter setting.")
		.def("setImuAccelFilter", &dv::io::camera::DVXplorerM::setImuAccelFilter, py::arg("filter"),
			"Set IMU accelerometer filter setting.")
		.def("getImuAccelRange", &dv::io::camera::DVXplorerM::getImuAccelRange, "Get current IMU accelerometer range.")
		.def("setImuAccelRange", &dv::io::camera::DVXplorerM::setImuAccelRange, py::arg("range"),
			"Set IMU accelerometer range.")
		.def("getImuGyroDataRate", &dv::io::camera::DVXplorerM::getImuGyroDataRate,
			"Get current IMU gyroscope data rate.")
		.def("setImuGyroDataRate", &dv::io::camera::DVXplorerM::setImuGyroDataRate, py::arg("dataRate"),
			"Set IMU gyroscope data rate.")
		.def("getImuGyroFilter", &dv::io::camera::DVXplorerM::getImuGyroFilter,
			"Get current IMU gyroscope filter setting.")
		.def("setImuGyroFilter", &dv::io::camera::DVXplorerM::setImuGyroFilter, py::arg("filter"),
			"Set IMU gyroscope filter setting.")
		.def("getImuGyroRange", &dv::io::camera::DVXplorerM::getImuGyroRange, "Get current IMU gyroscope range.")
		.def("setImuGyroRange", &dv::io::camera::DVXplorerM::setImuGyroRange, py::arg("range"),
			"Set IMU gyroscope range.");
}
