#include "../../include/dv-processing/version.hpp"

// clang-format off
#define PYBIND11_DETAILED_ERROR_MESSAGES
#include <pybind11/pybind11.h>

// Pybind type casters.
#include <pybind11/chrono.h>
#include <pybind11/eigen.h>
#include <pybind11/functional.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>

#include "../external/pybind11_opencv_numpy/ndarray_converter.h"

// Custom type casters.
#include "binding-utils/opencv.hpp"
#include "data/geometry_types_base.hpp"
// clang-format on

#include "camera/calibration_set.hpp"
#include "camera/calibrations/camera_calibration.hpp"
#include "camera/calibrations/imu_calibration.hpp"
#include "camera/calibrations/stereo_calibration.hpp"
#include "camera/camera_geometry.hpp"
#include "camera/stereo_geometry.hpp"
#include "cluster/mean_shift/event_store_adaptor.hpp"
#include "containers/kd_tree/event_store_adaptor.hpp"
#include "core/core.hpp"
#include "core/event.hpp"
#include "core/filters.hpp"
#include "core/frame/accumulator.hpp"
#include "core/frame/edge_map_accumulator.hpp"
#include "core/multi_stream_slicer.hpp"
#include "core/stereo_event_stream_slicer.hpp"
#include "core/time.hpp"
#include "core/utils.hpp"
#include "data/bounding_box_base.hpp"
#include "data/depth_event_base.hpp"
#include "data/depth_frame_base.hpp"
#include "data/event_base.hpp"
#include "data/frame_base.hpp"
#include "data/generate.hpp"
#include "data/imu_base.hpp"
#include "data/landmark_base.hpp"
#include "data/pose_base.hpp"
#include "data/timed_keypoint_base.hpp"
#include "data/trigger_base.hpp"
#include "data/types.hpp"
#include "depth/semi_dense_stereo_matcher.hpp"
#include "depth/sparse_event_block_matcher.hpp"
#include "features/arc_corner_detector.hpp"
#include "features/event_blob_detector.hpp"
#include "features/event_combined_lk_tracker.hpp"
#include "features/event_feature_lk_tracker.hpp"
#include "features/feature_detector.hpp"
#include "features/feature_tracks.hpp"
#include "features/image_feature_lk_tracker.hpp"
#include "features/image_pyramid.hpp"
#include "features/keypoint_resampler.hpp"
#include "features/mean_shift_tracker.hpp"
#include "features/redetection_strategy.hpp"
#include "features/tracker_base.hpp"
#include "imgproc/imgproc.hpp"
#include "io/camera.hpp"
#include "io/data/IOHeader.hpp"
#include "io/data_read_handler.hpp"
#include "io/davis.hpp"
#include "io/discovery.hpp"
#include "io/dvs128.hpp"
#include "io/dvxplorer.hpp"
#include "io/dvxplorer_m.hpp"
#include "io/input_base.hpp"
#include "io/mono_camera_recording.hpp"
#include "io/mono_camera_writer.hpp"
#include "io/network_reader.hpp"
#include "io/network_writer.hpp"
#include "io/stereo_camera_recording.hpp"
#include "io/stereo_camera_writer.hpp"
#include "io/stream.hpp"
#include "kinematics/linear_transformer.hpp"
#include "kinematics/motion_compensator.hpp"
#include "kinematics/pixel_motion_predictor.hpp"
#include "kinematics/transformation.hpp"
#include "measurements/depth.hpp"
#include "noise/background_activity_noise_filter.hpp"
#include "noise/fast_decay_noise_filter.hpp"
#include "noise/frequency_filters.hpp"
#include "noise/k_noise_filter.hpp"
#include "visualization/colors.hpp"
#include "visualization/event_visualizer.hpp"
#include "visualization/pose_visualizer.hpp"

namespace py = pybind11;

PYBIND11_MODULE(dv_processing, m, py::mod_gil_not_used()) {
	m.attr("__version__") = fmt::format(
		"{}.{}.{}", DV_PROCESSING_VERSION_MAJOR, DV_PROCESSING_VERSION_MINOR, DV_PROCESSING_VERSION_PATCH);

	NDArrayConverter::init_numpy();

	py::class_<std::filesystem::path>(m, "Path").def(py::init<std::string>(), py::arg("path"));
	py::implicitly_convertible<std::string, std::filesystem::path>();

	/**
	 * Binds subfolder camera
	 */
	{
		// Binding camera
		auto m_camera = m.def_submodule("camera");
		bind_calibration_set(m_camera);
		bind_camera_geometry(m_camera);
		bind_stereo_geometry(m_camera);

		auto m_calibrations = m_camera.def_submodule("calibrations");
		bind_calibration_camera_calibration(m_calibrations);
		bind_calibration_imu_calibration(m_calibrations);
		bind_calibration_stereo_calibration(m_calibrations);
	}

	/**
	 * Binds subfolder cluster
	 */
	{
		auto m_cluster    = m.def_submodule("cluster");
		auto m_mean_shift = m_cluster.def_submodule("mean_shift");
		bind_mean_shift_store_adaptor(m_mean_shift);
	}

	/**
	 * Binds subfolder containers
	 */
	{
		auto m_containers = m.def_submodule("containers");
		auto m_kd_tree    = m.def_submodule("kd_tree");
		bind_event_store_adaptor(m_kd_tree);
	}

	/**
	 * Binds subfolder core
	 */
	{
		bind_data_accumulator(m);
		bind_data_edge_map_accumulator(m);
		bind_core(m);
		bind_event(m);
		bind_filters(m);
		bind_multi_stream_slicer(m);
		bind_stereo_event_stream_slicer(m);
		bind_time(m);
		bind_utils(m);
	}

	/**
	 * Binds subfolder data
	 */
	{
		bind_bounding_box_base(m);
		bind_depth_event_base(m);
		bind_depth_frame_base(m);
		bind_event_base(m);
		bind_frame_base(m);
		// includes geometry_types_base typecasts above
		bind_imu_base(m);
		bind_landmark_base(m);
		bind_pose_base(m);
		bind_timed_keypoint_base(m);
		bind_trigger_base(m);

		auto m_data     = m.def_submodule("data");
		auto m_generate = m_data.def_submodule("generate");
		bind_generate(m_generate);

		auto m_types = m.def_submodule("types");
		bind_types(m_types);
	}

	/**
	 * Binds subfolder depth
	 */
	{
		bind_semi_dense_stereo_matcher(m);
		bind_sparse_event_block_matcher(m);
	}

	/**
	 * Binds subfolder features
	 */
	{
		auto m_features = m.def_submodule("features");

		bind_image_feature_lk_tracker(m_features); // Must be build first since it includes LucasKanadeConfig

		bind_arc_corner_detector(m_features);
		bind_event_blob_detector(m_features);
		bind_event_combined_lk_tracker(m_features);
		bind_event_feature_lk_tracker(m_features);
		bind_feature_detector(m_features);
		bind_feature_tracks(m_features);
		bind_image_pyramid(m_features);
		bind_keypoint_resampler(m_features);
		bind_mean_shift_tracker(m_features);
		bind_redetection_strategy(m_features);
		bind_tracker_base(m_features);
	}

	/**
	 * Binds subfolder imgproc
	 */
	{
		auto m_imgproc = m.def_submodule("imgproc");
		bind_imgproc(m_imgproc);
	}

	/**
	 * Binds subfolder io
	 */
	{
		bind_data_io_header(m);

		auto m_io = m.def_submodule("io");
		bind_data_read_handler(m_io);
		bind_stream(m_io);

		bind_input_base(m_io);
		bind_mono_camera_recording(m_io);
		bind_mono_camera_writer(m_io);
		bind_network_reader(m_io);
		bind_network_writer(m_io);
		bind_stereo_camera_recording(m_io);
		bind_stereo_camera_writer(m_io);

		auto m_camera = m_io.def_submodule("camera");
		bind_camera_base(m_camera);
		bind_discovery(m_camera);
		bind_dvs128(m_camera);
		bind_davis(m_camera);
		bind_dvxplorer(m_camera);
		bind_dvxplorer_m(m_camera);
	}

	/**
	 * Binds subfolder kinematics
	 */
	{
		auto m_kinematics = m.def_submodule("kinematics");
		bind_linear_transformer(m_kinematics);
		bind_motion_comensator(m_kinematics);
		bind_pixel_motion_predictor(m_kinematics);
		bind_transformation(m_kinematics);
	}

	/**
	 * Binds subfolder measurements
	 */
	{
		auto m_measurements = m.def_submodule("measurements");
		bind_depth(m_measurements);
	}

	/**
	 * Binds subfolder noise
	 */
	{
		auto m_noise = m.def_submodule("noise");
		bind_background_activity_noise_filter(m_noise);
		bind_k_noise_filter(m_noise);
		bind_fast_decay_noise_filter(m_noise);
		bind_frequency_filters(m_noise);
	}

	/**
	 * Binds subfolder visualization
	 */
	{
		auto m_visualization = m.def_submodule("visualization");
		bind_event_visualizer(m_visualization);
		bind_pose_visualizer(m_visualization);

		auto m_colors = m_visualization.def_submodule("colors");
		bind_colors(m_colors);
	}
}
