#ifndef MODULE_LOADER_INCLUDED
#define MODULE_LOADER_INCLUDED

#include <rofi/connector.hpp>
#include <rofi/module.hpp>
#include <rofi/voxel.hpp>
#include <rofi/voxel_graph.hpp>

#include <boost/json.hpp>

#include <filesystem>
#include <set>
#include <tuple>

/* EXPORT */

void export_module(voxel_graph_ptr voxel_graph, const char* file_name, int id, const char* type_name);

void export_components(boost::json::array &components, voxel_graph_ptr &voxel_graph, int parent_id,
                       std::map<voxel_ptr, std::size_t> &voxel_index, std::map<connector_ptr, std::size_t> &connector_index);
bool export_component_voxel(boost::json::array &components, const voxel_ptr &voxel, int parent_id);
bool export_component_connector(boost::json::array &components, const connector_ptr &connector, int parent_id);

void export_joints_pad(boost::json::array &joints, voxel_graph_ptr &voxel_graph, 
                   const std::map<connector_ptr, std::size_t> &connector_index);
void export_joint_pad(boost::json::array &joints, connector_ptr connector, std::size_t from_id, std::size_t to_id, const glm::vec3 translation);
std::pair<std::size_t, std::size_t> get_width_height(const std::vector<connector_ptr> &connectors);
boost::json::array create_STD_matrix_pad(const glm::vec3 translation);

void export_joints(boost::json::array &joints, voxel_graph_ptr &voxel_graph,
                   const std::map<voxel_ptr, std::size_t> &voxel_index, const std::map<connector_ptr, std::size_t> &connector_index);
void export_joints_rotation(boost::json::array &joints, const std::vector<voxel_ptr> &voxels,
                   const std::map<voxel_ptr, std::size_t> &voxel_index, const std::map<connector_ptr, std::size_t> &connector_index); 
void export_joints_shared_rotation(boost::json::array &joints, const std::vector<voxel_ptr> &voxels,
                   const std::map<voxel_ptr, std::size_t> &voxel_index);
void export_joints_fixed_open(boost::json::array &joints, voxel_graph_ptr &voxel_graph,
                   const std::map<voxel_ptr, std::size_t> &voxel_index, const std::map<connector_ptr, std::size_t> &connector_index);


void export_joint(boost::json::object &array_element, const connector_ptr &connector, rofi::connector_type expected_type);
void export_joint_axis(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);
void export_joint_limits(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);
void export_joint_modulo(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);
void export_joint_positions(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);
void export_joint_transform_matrix(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);
boost::json::array create_STD_matrix(const connector_ptr &connector, bool &is_identity);
boost::json::array create_shared_rotation_postMatrix(const connector_ptr &connector);
void export_joint_type(boost::json::object &joint, const connector_ptr &connector, rofi::connector_type expected_type);

bool check_universal_module_voxel_graph(voxel_graph_ptr &voxel_graph);
void export_module_type(boost::json::object &module_object, voxel_graph_ptr &voxel_graph, const char* type_name);

/* IMPORT */

std::vector<module_ptr> import_saved_configurations_module();
module_ptr import_module(const std::filesystem::path file_path);
void get_module_data(const boost::json::object &json_object, boost::json::array &components_array, boost::json::value &id_value, 
                     boost::json::array &joints_array, boost::json::string &type_string);

voxel_graph_ptr import_passive_cube(const boost::json::array &components_array, const boost::json::array &joints_array, std::map<objectbase_ptr, uint64_t> &component_ids);
voxel_graph_ptr import_universal(const boost::json::array &components_array, const boost::json::array &joints_array, std::map<objectbase_ptr, uint64_t> &component_ids);
voxel_graph_ptr import_pad(const boost::json::array &components_array, const boost::json::array &joints_array, std::map<objectbase_ptr, uint64_t> &component_ids);

std::set<uint64_t> get_component_ids(const boost::json::array &components_array, const std::string &&component_type);
std::vector<std::tuple<uint64_t, uint64_t, glm::mat4>> get_rigid_joints(const boost::json::array &joints_array, 
                                                                        const std::set<uint64_t> &body_ids, 
                                                                        const std::set<uint64_t> &roficom_ids);

std::map<uint64_t, objectbase_ptr> import_rigid_joints(voxel_graph_ptr vg, 
                                                       const std::vector<std::tuple<uint64_t, uint64_t, glm::mat4>> &from_to_std, 
                                                       const std::map<uint64_t, objectbase_ptr> &from_components);

rofi::side std_to_side(const glm::mat4 &std_matrix);

const std::map<uint64_t, objectbase_ptr> import_um_bodies(voxel_graph_ptr vg, const std::set<uint64_t> &body_ids);
const std::map<uint64_t, objectbase_ptr> import_um_shoes(voxel_graph_ptr vg,
                                                         const std::vector<std::tuple<uint64_t, uint64_t, float, float>> &from_to_min_max, 
                                                         const std::map<uint64_t, objectbase_ptr> &id_body_pairs);

std::vector<std::tuple<uint64_t, uint64_t, float, float>> get_rotation_joints(const boost::json::array &joints_array, const std::set<uint64_t> &body_ids);
float get_limit(const boost::json::value &limit_value);

std::pair<uint64_t, uint64_t> get_pad_width_height_from_joints(const std::size_t roficom_count, const std::vector<std::tuple<uint64_t, uint64_t, glm::mat4>> &from_to_std);

#endif