#ifndef EDITOR_INCLUDED
#define EDITOR_INCLUDED

#include <controls/control_hub.hpp>
#include <edit/scene.hpp>
#include <filein/module_loader.hpp>
#include <gfx/mesh.hpp>
#include <osi/keyboard.hpp>
#include <osi/mouse.hpp>
#include <osi/timer.hpp>
#include <osi/window.hpp>
#include <rofi/connector.hpp>
#include <rofi/module.hpp>
#include <rofi/rofiworld.hpp>
#include <rofi/voxel.hpp>
#include <rofi/voxel_graph.hpp>
#include <utils/math.hpp>

#include <array>
#include <memory>
#include <set>
#include <string>

using ctrlhub_ptr = std::shared_ptr<ControlHub>;
using scene_ptr = std::shared_ptr<Scene>;

using connector_ptr = std::shared_ptr<rofi::Connector>;
using voxel_ptr = std::shared_ptr<rofi::Voxel>;
using voxel_weak_ptr = std::weak_ptr<rofi::Voxel>;

namespace edit
{
    enum class phase { one, two };
    enum class mode_I { build, spectate, debug };
    enum class build_mode_I { cube, universal, pad, unknown };
    enum class mode_II { build, spectate, debug };
    enum class build_mode_II { add_module, connect_module, manipulate_module, rotate_component };
    enum class state { select, move, rotate, plus };

class UIData
{
public:
    double current_second = 0;
    double ui_error_start_second = 0;
    bool help_controls = false;
    bool face_highlight = false;
    bool vocal_error = false;
    double vocal_error_start_second;
    bool export_file_prompt = false;
    bool export_file_visible = false;
    bool export_file_error = false;
    bool import_file_prompt = false;
    bool import_file_visible = false;
    bool import_file_error = false;
    bool save_module_prompt = false;
    bool save_module_visible = false;
    bool save_module_error = false;
    bool save_rofiworld_prompt = false;
    bool save_rofiworld_visible = false;
    bool save_rofiworld_error = false;
    bool save_rofiworld_use_current_name = true;
    bool start_new_rofiworld_visible = false;
    bool start_new_rofiworld_prompt = false;
    bool select_rofiworld_prompt = false;
    bool export_rofiworld_use_selected_space_joint = false;
    char name_buffer[256] = {0};
    int module_id = 0;
    int add_module_list_index = -1;
    float module_translation_step = 1.0f;
    float module_rotation_step = 1.0f;
    int module_rotation_input = 0;
    float component_rotation_step = 1.0f;
    
    connector_ptr connector;
    voxel_ptr voxel;

    rofiworld_ptr selected_rofiworld;

    /* Ref. variables, need to be initialized in constructor */
    phase &editor_phase;
    mode_I &editor_mode_I;
    mode_II &editor_mode_II;
    build_mode_I &editor_build_mode_I;
    build_mode_II &editor_build_mode_II;
    state &control_state;
    bool &ui_visible;

    rofi::component &chosen_component;
    rofi::connector_type &chosen_con_type;
    std::vector<rofi::component> &allowed_components;
    std::vector<module_ptr> &modules;
    module_ptr &selected_add_module;
    module_ptr &selected_module;
    rofiworld_ptr &rofiworld;
    std::vector<rofiworld_ptr> &rofiworld_selection;
    bool &voxel_selected;
    bool &connector_selected;
    bool &primary_axis;

    bool &is_connecting_modules;
    bool &can_connect_modules;
    rofi::cardinal &selected_cardinality;
    std::pair<connector_ptr, connector_ptr> &selected_connectors;
    std::pair<module_ptr, module_ptr> &selected_modules_connect;
    bool &is_moving_module;
    glm::vec3 &module_position;
    bool &is_rotating_module;
    bool &is_resetting_module_rotation;
    int &coordinate_space_selection;
    glm::vec3 &module_rotation_angles;
    bool &is_rotating_component;
    float &component_rotation_angle;

    bool &is_creating_pad;
    std::size_t &selected_pad_width;
    std::size_t &selected_pad_height;

    std::string &error_msg;
    double &error_start_second;

    UIData(phase &_editor_phase, mode_I &_editor_mode_I, mode_II &_editor_mode_II, build_mode_I &_editor_build_mode_I, build_mode_II &_editor_build_mode_II, 
           state &_control_state, bool &_ui_visible, rofi::component &_chosen_component, rofi::connector_type &_chosen_con_type, 
           std::vector<rofi::component> &_allowed_components, std::vector<module_ptr> &_modules, module_ptr &_selected_add_module, module_ptr &_selected_module, 
           rofiworld_ptr &_rofiworld, std::vector<rofiworld_ptr> &_rofiworld_selection, bool &_is_connecting_modules, bool &_can_connect_modules,
           rofi::cardinal &_selected_cardinality, std::pair<connector_ptr, connector_ptr> &_selected_connectors, std::pair<module_ptr, module_ptr> &_selected_modules_connect, 
           bool &_connector_selected, bool &_voxel_selected, 
           bool &_is_moving_module, glm::vec3 &_module_position, int &_coordinate_space_selection, bool &_is_rotating_module, bool &_is_resetting_module_rotation,
           glm::vec3 &_module_rotation_angles, bool &_is_rotating_component, float &_component_rotation_angle, 
           bool &_is_creating_pad, std::size_t &_selected_pad_width, std::size_t &_selected_pad_height,
           std::string &_error_msg, double &_error_start_second, bool &_primary_axis)
    :
    editor_phase{_editor_phase},
    editor_mode_I{_editor_mode_I},
    editor_mode_II{_editor_mode_II},
    editor_build_mode_I{_editor_build_mode_I},
    editor_build_mode_II{_editor_build_mode_II},
    control_state{_control_state},
    ui_visible{_ui_visible},
    chosen_component{_chosen_component},
    chosen_con_type{_chosen_con_type},
    allowed_components{_allowed_components},
    modules{_modules},
    selected_add_module{_selected_add_module},
    selected_module{_selected_module},
    rofiworld{_rofiworld},
    rofiworld_selection{_rofiworld_selection},
    is_connecting_modules{_is_connecting_modules},
    can_connect_modules{_can_connect_modules},
    selected_cardinality{_selected_cardinality},
    selected_connectors{_selected_connectors},
    selected_modules_connect{_selected_modules_connect},
    connector_selected{_connector_selected},
    voxel_selected{_voxel_selected},
    is_moving_module{_is_moving_module},
    module_position{_module_position},
    coordinate_space_selection{_coordinate_space_selection},
    is_rotating_module{_is_rotating_module},
    is_resetting_module_rotation{_is_resetting_module_rotation},
    is_rotating_component{_is_rotating_component},
    component_rotation_angle{_component_rotation_angle},
    module_rotation_angles{_module_rotation_angles},
    is_creating_pad{_is_creating_pad},
    selected_pad_width{_selected_pad_width},
    selected_pad_height{_selected_pad_height},
    error_msg{_error_msg},
    error_start_second{_error_start_second},
    primary_axis{_primary_axis}
    {}
};

class Editor
{
    bool editor_running = false;
    bool connector_selected = false;
    bool voxel_selected = false;
    bool primary_axis = true;
    phase editor_phase = phase::one;
    mode_I editor_mode_I = mode_I::build;
    mode_II editor_mode_II = mode_II::build;
    build_mode_I editor_build_mode_I = build_mode_I::cube;
    build_mode_II editor_build_mode_II = build_mode_II::add_module;
    phase prev_phase = editor_phase;
    mode_I prev_mode_I = editor_mode_I;
    mode_II prev_mode_II = editor_mode_II;
    build_mode_I prev_build_mode_I = editor_build_mode_I;
    build_mode_II prev_build_mode_II = editor_build_mode_II;

    bool is_placing_module = false;
    bool is_removing_module = false;
    bool is_connecting_modules = false;
    bool is_disconnecting_modules = false;
    bool is_moving_module = false;
    bool is_rotating_module = false;
    bool is_resetting_module_rotation = false;
    int coordinate_space_selection = 0;
    bool is_rotating_component = false;

    glm::vec3 module_position = glm::vec3(0.0f);
    glm::vec3 module_rotation_angles = glm::vec3(0.0f);
    float component_rotation_angle = 0.0f;

    state control_state = state::select;
    bool ui_visible = true;

    rofi::component chosen_component = rofi::component::voxel; 
    rofi::connector_type chosen_con_type = rofi::connector_type::fixed;
    std::vector<rofi::component> allowed_components = { rofi::component::voxel };
    bool is_creating_pad = false;
    std::size_t selected_pad_width = 1;
    std::size_t selected_pad_height = 1;
    std::string error_msg;
    double error_start_second;
    char module_type_buffer[256] = {0};

    UIData ui = UIData{editor_phase, editor_mode_I, editor_mode_II, editor_build_mode_I, editor_build_mode_II, control_state, ui_visible, 
                       chosen_component, chosen_con_type, allowed_components, modules, selected_add_module, selected_module, rofiworld, rofiworld_selection,
                       is_connecting_modules, can_connect_modules, selected_cardinality, selected_connectors, selected_modules_connect, 
                       connector_selected, voxel_selected, is_moving_module, module_position, coordinate_space_selection, 
                       is_rotating_module, is_resetting_module_rotation, module_rotation_angles, is_rotating_component, component_rotation_angle, 
                       is_creating_pad, selected_pad_width, selected_pad_height,
                       error_msg, error_start_second, primary_axis};

    node_ptr hover_nearest_node = nullptr;
    node_ptr selection_previous_frame = nullptr;
    bool object_moving = false;
    glm::vec3 prev_plane_intersect = glm::vec3(0.0f);

    const osi::Keyboard &keyboard;
    const osi::Mouse &mouse;
    const osi::Timer &timer;
    const osi::Window &window;

    scene_ptr scene;
    ctrlhub_ptr controls;

    voxel_graph_ptr voxel_graph;
    std::map<build_mode_I, voxel_graph_ptr> module_type_voxel_graphs;

    /* Modules available to be used in RofiWorld creation */
    std::vector<module_ptr> modules = import_saved_configurations_module();

    rofiworld_ptr rofiworld = std::make_shared<rofi::RofiWorld>();
    std::vector<rofiworld_ptr> rofiworld_selection;

    module_ptr selected_add_module;
    module_ptr prev_selected_add_module;
    module_ptr selected_module;

    node_ptr prev_rebuild_node;
    bool can_rotate_component = true;
    module_ptr rotation_locked_module;

    bool rofi_invalid_state = false;
    std::set<module_ptr> collided_modules;

    rofi::cardinal selected_cardinality = rofi::cardinal::North;
    std::pair<connector_ptr, connector_ptr> selected_connectors = {nullptr, nullptr};
    std::pair<glm::vec3, node_ptr> first_connector_highlights = {glm::vec3(0.0f), nullptr};
    std::pair<glm::vec3, node_ptr> second_connector_highlights = {glm::vec3(0.0f), nullptr};
    std::pair<module_ptr, module_ptr> selected_modules_connect = { nullptr, nullptr };
    bool can_connect_modules = true;

    std::vector<objectbase_ptr> selected_space_joints;

    // Default Mesh

    mesh_ptr voxel_mesh = Mesh::loadMesh("./data/models/rofi/cube_new_scale.obj");
    mesh_ptr body_mesh = Mesh::loadMesh("./data/models/rofi/body_new_scale.obj", false, false);
    mesh_ptr pad_board_mesh = Mesh::loadMesh("./data/models/rofi/pad_board.obj", false, false);

    mesh_ptr fixed_connector_mesh = Mesh::loadMesh("./data/models/rofi/pad_connector.obj", false, false);
    mesh_ptr open_connector_mesh = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj", false, false);
    mesh_ptr connected_connector_mesh = Mesh::loadMesh("./data/models/rofi/connector_connected_new_scale.obj", false, false);
    mesh_ptr shared_connector_mesh = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj");
    mesh_ptr empty_connector_mesh = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj", false, false);

    mesh_ptr empty_shoe_mesh = Mesh::loadMesh("./data/models/rofi/shoe_new_scale.obj", false, false);

    // Invalid Mesh

    mesh_ptr voxel_mesh_invalid = Mesh::loadMesh("./data/models/rofi/cube_new_scale.obj");
    mesh_ptr body_mesh_invalid = Mesh::loadMesh("./data/models/rofi/body_new_scale.obj", false, false);
    mesh_ptr pad_board_mesh_invalid = Mesh::loadMesh("./data/models/rofi/pad_board.obj", true, false);

    mesh_ptr fixed_connector_mesh_invalid = Mesh::loadMesh("./data/models/rofi/pad_connector.obj", false, false);
    mesh_ptr open_connector_mesh_invalid = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj", false, false);
    mesh_ptr connected_connector_mesh_invalid = Mesh::loadMesh("./data/models/rofi/connector_connected_new_scale.obj", false, false);
    mesh_ptr shared_connector_mesh_invalid = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj");
    mesh_ptr empty_connector_mesh_invalid = Mesh::loadMesh("./data/models/rofi/connector_new_scale.obj", false, false);

    mesh_ptr empty_shoe_mesh_invalid = Mesh::loadMesh("./data/models/rofi/shoe_new_scale.obj", false, false);

    const std::vector<mesh_ptr> voxel_meshes = { voxel_mesh, voxel_mesh_invalid };
    const std::vector<mesh_ptr> body_meshes = { body_mesh, body_mesh_invalid };
    const std::vector<mesh_ptr> pad_board_meshes = { pad_board_mesh, pad_board_mesh_invalid };
    const std::vector<mesh_ptr> fixed_connector_meshes = { fixed_connector_mesh, fixed_connector_mesh_invalid };
    const std::vector<mesh_ptr> open_connector_meshes = { open_connector_mesh, open_connector_mesh_invalid };
    const std::vector<mesh_ptr> connected_connector_meshes = { connected_connector_mesh, connected_connector_mesh_invalid };
    const std::vector<mesh_ptr> shared_connector_meshes = { shared_connector_mesh, shared_connector_mesh_invalid };
    const std::vector<mesh_ptr> empty_connector_meshes = { empty_connector_mesh, empty_connector_mesh_invalid };
    const std::vector<mesh_ptr> empty_shoe_meshes = { empty_shoe_mesh, empty_shoe_mesh_invalid };

public:
    Editor(const osi::Keyboard &_keyboard,
           const osi::Mouse &_mouse,
           const osi::Timer &_timer,
           const osi::Window &_window,
           scene_ptr _scene,
           ctrlhub_ptr _controls)
            : keyboard{_keyboard}, mouse{_mouse}, timer{_timer}, window{_window}, 
              scene{_scene}, controls{_controls}
    { 
        // Default Mesh
        voxel_mesh->setMaterial(mat::yellow_plastic);
        body_mesh->setMaterial(mat::black_plastic);
        pad_board_mesh->setMaterial(mat::brown_rubber);

        fixed_connector_mesh->setMaterial(mat::blue_plastic);
        open_connector_mesh->setMaterial(mat::blue_plastic);
        connected_connector_mesh->setMaterial(mat::red_plastic);
        shared_connector_mesh->setMaterial(mat::black_plastic);
        empty_connector_mesh->setMaterial(mat::empty_material);

        empty_shoe_mesh->setMaterial(mat::yellow_plastic);

        //Invalid Mesh
        voxel_mesh_invalid->setMaterial(mat::red_highlight);
        body_mesh_invalid->setMaterial(mat::red_highlight);
        pad_board_mesh_invalid->setMaterial(mat::red_highlight);

        fixed_connector_mesh_invalid->setMaterial(mat::red_highlight);
        open_connector_mesh_invalid->setMaterial(mat::red_highlight);
        connected_connector_mesh_invalid->setMaterial(mat::red_highlight);
        shared_connector_mesh_invalid->setMaterial(mat::red_highlight);
        empty_connector_mesh_invalid->setMaterial(mat::red_highlight);

        empty_shoe_mesh_invalid->setMaterial(mat::red_highlight);

        enterPhaseOne();

        linkModulesNodes(modules);
        setModulesNodesHierarchy(modules);
        setModulesMesh(modules);
    }

    bool &getEditorRunning() {return editor_running; }
    bool isRunning() const { return editor_running; }
    void setRunning(bool _editor_running) { editor_running = _editor_running; }

    // bool isConnectorSelected() const { return connector_selected; }
    // bool isVoxelSelected() const { return voxel_selected; }

    UIData &getUIData() { return ui; }

    phase getPhase() const { return editor_phase; }
    mode_I getMode_I() const { return editor_mode_I; }
    mode_II getMode_II() const { return editor_mode_II; }
    build_mode_I getModuleMode() const { return editor_build_mode_I; }
    build_mode_II getRofiWorldMode() const { return editor_build_mode_II; }

    void setError(std::string message);

    void manageUI();
    void manageUISaveModule();
    void manageUISaveRofiWorld();
    void manageUIImportRofiWorld();
    void manageUIClearRofiWorld();
    void manageUISelectRofiWorld();

    bool checkModuleExport(const voxel_graph_ptr &voxel_graph, const build_mode_I build_mode);

    void resetBuildModeIIVariables();

    state &getControlState() { return control_state; }

    bool isObjectMoving() const { return object_moving; }
    void setObjectMoving(bool _object_moving) { object_moving = _object_moving; } 

    glm::vec3 getPrevPlaneIntersect() const { return prev_plane_intersect; }
    void setPrevPlaneIntersect(glm::vec3 _prev_plane_intersect) { prev_plane_intersect = _prev_plane_intersect; }

    void update();
    void updateEnvironment();

private:
    /* VARIOUS STATE CHANGE AND SELECTION */
    void changePhase();
    void selectPhase();
    void enterPhase(const phase previous_phase);
    void enterPhaseOne();
    void enterPhaseTwo();

    void changeMode();
    void changeModeI();
    void changeModeII();
    void exitModeI(const mode_I previous_mode, const build_mode_I previous_build_mode);
    void exitBuildModeI(const build_mode_I previous_build_mode);
    void exitModeII(const mode_II previous_mode, const build_mode_II previous_build_mode);
    void selectModePhaseOne();
    void selectModePhaseTwo();

    void changeBuildModeI();
    void changeBuildModeII();
    void selectBuildModeI();
    void selectBuildModeII();

    /* DO ACTION */
    void doBuildCubeAction();
    void doBuildUniversalAction();
    void doBuildPadAction();
    void doBuildUnknownAction();

    void updateAllowedComponents();
    void updateBuildHint();
    bool checkBuildValid();
    bool checkCubeBuildValid();
    bool checkUniversalBuildValid();
    bool checkPadBuildValid();
    bool checkUnknownBuildValid();

    void doSpectateAction();
    void doDebugAction();

    /* CONTROL */
    void controlBuild();

    void controlComponentSelection();
    void controlCubeComponentSelection();
    void controlUniversalComponentSelection();
    void controlPadComponentSelection();
    void controlUnknownComponentSelection();

    void controlSpectate();
    void controlDebugSelection();

    /* EXIT */
    void exitBuild();
    void exitSpectate();

    /* GENERAL EDIT */
    void hoverOver();
    void selectObject();
    void rotateObject();
    void moveObject(node_ptr node_to_move);
    void useObjectController(node_ptr node, obj_control_ptr controller,  camera_ptr camera, glm::vec3* ray_position = nullptr, glm::vec3* ray_direction = nullptr);
    void addObject();

    /* ROFI MANAGEMENT */

    void initializeDefaultVoxelGraphs();
    void initializeCube();
    void initializeUniversal();
    void initializePad();
    void initializeUnknown();
    void createPad(std::size_t &width, std::size_t &height);
    void generatePad(std::size_t &width, std::size_t &height);
    void setVoxelGraphToScene(voxel_graph_ptr prev_vg, voxel_graph_ptr new_vg);
    void addVoxelGraphToScene(voxel_graph_ptr vg);
    void removeVoxelGraphFromScene(voxel_graph_ptr vg);
    void resetModuleVoxelGraph(const build_mode_I type);
    void addToModules(std::string name, std::string type);
    void removeFromModules(voxel_graph_ptr vg);
    void removeFromModules(module_ptr module);

    void convertComponentToConType();
    void setModuleTypeBuffer(const build_mode_I build_mode);
    bool isConType(rofi::component component);

    bool isVoxel(node_ptr selection) const;
    bool isConnector(node_ptr selection) const;

    voxel_ptr getVoxel(node_ptr selection) const;
    connector_ptr getConnector(node_ptr selection) const;
    rofi::connector_type &getConnectorType() { return chosen_con_type; }
    glm::vec3 getNewItemLocalDirection() const;

    glm::vec3 generateAxisDirection();

    void addConnector();
    bool checkConnectorRequest();
    bool checkConnectorRequestOnVoxel(node_ptr selection);
    bool checkConnectorRequestOnRotationConnector(node_ptr selection);
    void addConnectorMesh(node_ptr node, rofi::connector_type con_type);
    connector_ptr createConnector(node_ptr node, glm::vec3 voxel_local_direction);
    node_ptr createConnectorNode(node_ptr selection, glm::vec3 voxel_local_direction);
    node_ptr createConnectorNode(connector_ptr connector);

    void addRotationConnectorSides(connector_ptr connector);

    void modifyRotationConnector(connector_ptr connector);
    void changeRotationConnectorMesh(node_ptr node, connector_ptr connector, glm::vec3 direction);

    node_ptr findConnectorNode(connector_ptr connector);

    void addVoxel();
    node_ptr addVoxelAtPosition(node_ptr node);
    node_ptr addVoxelAtPosition(glm::vec3 position, glm::quat rotation = glm::quat(1.0f, glm::vec3(0.0f)));
    bool checkVoxelRequest();
    void addVoxelMesh(node_ptr node);
    voxel_ptr createVoxel(node_ptr node);
    node_ptr createVoxelNode(connector_ptr connector);

    void removeComponent();
    void removeConnector();
    void removeVoxel();

    void seekOrphans(std::vector<node_ptr> &to_remove_nodes, 
                     std::vector<connector_ptr> &to_remove_connectors);
    void destroyOrphans(const std::vector<node_ptr> &to_remove_nodes, 
                        const std::vector<connector_ptr> &to_remove_connectors);
    void seekAndDestroyOrphans();

    void updateRoFIMesh(const voxel_graph_ptr vg);
    void toggleRoFIInvalidStateMesh(bool invalid, const std::set<module_ptr> &_modules);
    void setRoFIMesh(const voxel_graph_ptr vg, bool invalid_state = false);
    void setVoxelMesh(const voxel_ptr voxel, bool invalid_state = false);
    void setConnectorMesh(const connector_ptr connector, bool invalid_state = false);

    /* ROFI -- PHASE TWO */

    /* DO ACTION */
    void doAddModuleAction();
    void doConnectModuleAction();
    void doManipulateModuleAction();
    void doRotateComponentAction();

    /* CONNECT MODULE ACTION HELPERS */
    void updateSelectedConnectors();
    void updateConnectionHint();
    bool checkSelectedConnectorsValid();

    /* CONTROL PHASE II BUILD */
    void controlAddModule();
    void controlConnectModule();
    void controlManipulateModule();

    /* ADD MODULE ACTION HELPERS */
    void updateSelectedModule();
    void setModuleToMouse();

    /* ROTATE COMPONENT ACTION HELPERS */
    void updateSelectedComponent();
    bool checkSelectedComponentValid();
    void updateRotationHint();

    /* EXIT */
    void exitBuildModeII(const build_mode_II previous_build_mode);
    void exitAddModule();
    void exitConnectModule();
    void exitManipulateModule();
    void exitRotateComponent();

    /* ADD MODULE */
    void addModule();
    void placeModule(module_ptr module);
    void setModuleToScene(module_ptr prev_module, module_ptr new_module);
    void addModuleToScene(module_ptr module);
    void removeModuleFromScene(module_ptr module);

    /* CONNECT MODULE */
    void connectModule();
    void disconnectModule();
    bool checkConnectModuleValid(const connector_ptr fst_connector, const connector_ptr snd_connector);

    /* MANIPULATE MODULE */
    void manipulateModule();
    void selectModule();
    void removeModule(module_ptr module);
    
    void linkModulesNodes(const std::vector<module_ptr> &modules);
    void setModulesNodesHierarchy(const std::vector<module_ptr> &modules);
    void setModulesMesh(const std::vector<module_ptr> &modules);

    void rotateModule(module_ptr selected_module, glm::vec3 &rotation_angles);

    /* ROTATE COMPONENT */
    void rotateComponent();
    
    /* NODE TREE REBUILD */
    void setRofiWorldNodeTreeToScene(const std::vector<node_ptr> &prev_node_tree, const std::vector<node_ptr> &new_node_tree);
    void addRofiWorldNodeTreeToScene(const std::vector<node_ptr> &node_tree);
    void removeRofiWorldNodeTreeFromScene(const std::vector<node_ptr> &node_tree);

    bool rebuildRofiWorldNodeTree(module_ptr module, node_ptr root_node, rofiworld_ptr rofiworld);
    bool rebuildRofiWorldNodeTree(voxel_ptr voxel, node_ptr root_component_node, rofiworld_ptr rofiworld);

    /* COLLISION DETECTION */
    bool detectCollision(bool something_changed, rofiworld_ptr rofiworld);

    /* ROFIWORLD EXPORT */
    void pickSpaceJoints(rofiworld_ptr rofiworld, module_ptr initial_module);
    void pickSpaceJoint(rofiworld_ptr rofiworld, module_ptr module, std::set<module_ptr> &found_modules);
    connector_ptr pickSpaceJointUniversal(const module_ptr module);
    connector_ptr pickSpaceJointGeneric(const module_ptr module);

    void addToRofiWorlds(rofiworld_ptr rofiworld, const std::string name);
};

}

#endif