#ifndef MODULE_INCLUDED
#define MODULE_INCLUDED

#include <common/node.hpp>
#include <rofi/voxel_graph.hpp>

#include <map>
#include <memory>
#include <string>

namespace rofi
{

class Module;

using module_ptr = std::shared_ptr<Module>;

class Module
{
    node_ptr node;
    voxel_graph_ptr parts;
    
    std::string name;
    std::string type;

    std::map<objectbase_ptr, uint64_t> component_ids;

    std::map<connector_ptr, std::string> connector_docks;

    void makeNodeTree();
    void setConnectorDocks();
    void setConnectorDocksCube();
    void setConnectorDocksUniversal();
    void setComponentIDs();
    void setComponentIDsUniversal();
    void setComponentIDsGeneric();

    Module(const Module &other);
    Module(const Module &&other);

    Module& operator=(const Module &other);
    Module& operator=(const Module &&other);

public:
    Module(voxel_graph_ptr _parts, std::string &_name, std::string &_type, std::map<objectbase_ptr, uint64_t> &_component_ids);
    Module(voxel_graph_ptr _parts, std::string &&_name, std::string &&_type, std::map<objectbase_ptr, uint64_t> &&_component_ids);
    ~Module() = default;

    static module_ptr copy(const module_ptr other_module);
    static void setModuleNodeHierarchy(voxel_graph_ptr vg, std::string type);
    static void setModuleNodeHierarchyCube(voxel_graph_ptr vg);
    static void setModuleNodeHierarchyUniversal(voxel_graph_ptr vg);

    node_ptr getNode() const { return node; }
    voxel_graph_ptr getParts() const { return parts; }

    const std::string &getName() const { return name; }
    const std::string &getType() const { return type; }

    const std::map<objectbase_ptr, uint64_t> &getComponentIDs() const { return component_ids; }
    const uint64_t &getComponentID(objectbase_ptr component) const { return component_ids.at(component); }
    const objectbase_ptr &getComponentByID(uint64_t id) const { return std::find_if(component_ids.begin(), 
                                                                                    component_ids.end(), 
                                                                                    [&id] (const auto pair) { return pair.second == id; } )->first; }

    const std::map<connector_ptr, std::string> &getConnectorDocks() const { return connector_docks; }
    const std::string &getConnectorDock(connector_ptr connector) const { return connector_docks.at(connector); }
    const connector_ptr getConnectorByDock(const std::string &dock) const { return std::find_if(connector_docks.begin(), 
                                                                                                connector_docks.end(), 
                                                                                                [&dock] (const auto pair) { return pair.second == dock; } )->first; }
};

}

using module_ptr = std::shared_ptr<rofi::Module>;

#endif