def generate_templates(self):
        def get_name_from_key(key):
            return key.lower().replace('-', '_').replace('.', '_')

        def assign_dict_nested_path(dict, path, value):
            def get(d, keys):
                for key in keys:
                    if key not in d:
                        d[key] = OrderedDict()  # TODO Investigate? Might need to instance another object
                        #     # print("Assigned empty value to path %s" % path)
                    d = d[key]
                return d

            def set(d, keys, value):
                d = get(d, keys[:-1])
                d[keys[-1]] = value

            if '.' in path:
                set(dict, path.split('.'), value)
            else:
                dict[path] = value

        def recursive_dictionary_collect(template_dict, parent_key, data):
            for key, value in data.items():
                new_key = key if parent_key is None else "%s.%s" % (parent_key, key)
                if isinstance(value, dict) or isinstance(value, OrderedDict):
                    recursive_dictionary_collect(template_dict, new_key, value)
                else:
                    if isinstance(value, list):
                        depth = len(new_key.split('.')) * 2
                        value.append('mcresolverdepth=%s' % depth)
                    template_dict[new_key] = value

        flat_key_template = OrderedDict()

        default_pluginfile_data = yamlbro.load_yaml(self.generate_base_config_file)

        for key, value in default_pluginfile_data.items():
            if isinstance(value, dict) or isinstance(value, OrderedDict):
                # replica_dict[key] = value
                recursive_dictionary_collect(flat_key_template, key, value)
            else:
                if isinstance(value, list):
                    value.append(
                        'mcresolverdepth=1')  # Hack around the template, and add the depth to prepend to the node
                flat_key_template[key] = value

        template_default_node_values = OrderedDict()
        node_value_types = {}

        for key, value in flat_key_template.items():
            node_name = key
            if '.' in key:
                node_split = key.split('.')
                if len(node_split) >= 2:
                    node_name = ".".join(node_split[-2:])
                else:
                    node_name = node_split[-1]

            node_name = get_name_from_key(node_name)
            # print("Key [%s] Node-Name %s" % (key, node_name))
            # node_name = get_name_from_key(node_name)
            value_type = value.__class__.__name__

            try:
                tdval = type(value)(value)
                if value_type == "bool":
                    tdval = str(tdval).lower()

                template_default_node_values[node_name] = tdval
                node_value_types[node_name] = value_type
                node_type = tdval.__class__.__name__
            except:
                template_default_node_values[node_name] = value
                node_value_types[node_name] = value_type
                node_type = value_type.__class__.__name__

            flat_key_template[key] = "{{{{{node}}}}}".format(node=node_name)

        expanded_key_config_template = OrderedDict()

        for key, value in flat_key_template.items():
            assign_dict_nested_path(expanded_key_config_template, key, value)

        config_template = yaml.dump(expanded_key_config_template, default_flow_style=False, indent=2,
                                    width=1000)

        with open(self.generate_base_config_file, 'r') as default_configuration_data:
            default_plugin_config = default_configuration_data.read()

        config_template = restore_yaml_comments(config_template, default_plugin_config)

        # Loop through all the nodes and their values, then change the jinja2 template
        # Variable to follow the format required for that item..
        # The reason we do this is to re-assign types to values (via their template-names)
        # As by default they all are quoted and are treated as strings.
        # We don't want this!
        for node, type in node_value_types.items():
            if type == "bool" or type == "int" or type == "float":
                config_template = config_template.replace("'{{{{{node}}}}}'".format(node=node),
                                                          "{{{{{node}}}}}".format(node=node))
            elif type == "str":
                continue
            elif type == "list":
                # If the value of the item is a list
                # Then there's quite a few things we have to do in order to preserve the format of the file!
                # First of all involves getting the depth (via a default value generated and added into the list)
                # Though this won't wind up in the final template. We do this because we need to have
                # The valid number of spaces before the list items, otherwise YAML Will break.
                node_list_values = template_default_node_values[node].copy()
                depth = 0
                for line in node_list_values:
                    if 'mcresolverdepth' in line:
                        # The depth of the item will be indexed by this!
                        # Depth is determined by how many parent keys, are above the child node
                        # Multiplied by 2, for yaml conventions.
                        # Example: Top-Level lists only have 2 spaces
                        # Whereas a list under top.level.list would have 4
                        depth = int(line.split('=')[1])
                        break

                # Collect all the nodes list items that dont have a depth attribute
                node_list_values = [line for line in node_list_values if 'mcresolverdepth' not in line]
                # Then reassign this value to the template defaults,
                # as we don't want the depth to be inside the users
                template_default_node_values[node] = node_list_values
                # Next we generate a loop statement for inside the template
                # On our node, to assure all the items in the list are processed
                loop_statement = "\n{{% for {node}_item in {node} %}}{depth}- {{{{list_item}}}}\n{{% endfor %}}".format(
                    depth=' ' * depth, node=node
                )

                # Lastly, for this part, replace the previous {{node}} item with the new list-generating statement
                # That was created above.
                config_template = config_template.replace(" '{{{{{node}}}}}'".format(node=node),
                                                          loop_statement)

        # Get the file contents for the default template nodes and their values in a yaml output!
        defaults_file_contents = yaml.dump(template_default_node_values, default_flow_style=False)
        # Next up, we need to remove all the 'none' values inside of this file, as it's really not a good idea
        # to have yaml parse "none" values... It'd return a string. So we make them blank instead.
        defaults_file_contents = defaults_file_contents.replace(": none", ": ")
        write_file(os.path.join(self.output_folder, '%s-template.yml' % self.generate_plugin_name),
                   config_template)
        write_file(os.path.join(self.output_folder, '%s-defaults.yml' % self.generate_plugin_name),
                   defaults_file_contents)
def test_template_generation():
    def get_name_from_key(key):
        return key.lower().replace('-', '_').replace('.', '_')

    __dirname, __init_python_script = os.path.split(os.path.abspath(__file__))
    ess_file = os.path.join(__dirname, 'essentials.yml')

    with open(ess_file, 'r') as yaml_file:
        default_essentials_config = yaml.safe_load(yaml_file)

    essentials_config = default_essentials_config.copy()

    def assign_dict_nested_path(dict, path, value):
        def get(d, keys):
            for key in keys:
                if key not in d:
                    d[key] = OrderedDict()  # TODO Investigate? Might need to instance another object
                    #     # print("Assigned empty value to path %s" % path)
                d = d[key]
            return d

        def set(d, keys, value):
            d = get(d, keys[:-1])
            d[keys[-1]] = value

        if '.' in path:
            set(dict, path.split('.'), value)
        else:
            dict[path] = value

    flat_dictionary_template = OrderedDict()

    def recursive_dictionary_collect(template_dict, parent_key, data):
        for key, value in data.items():
            new_key = key if parent_key is None else "%s.%s" % (parent_key, key)
            if isinstance(value, dict) or isinstance(value, OrderedDict):
                recursive_dictionary_collect(template_dict, new_key, value)
            else:
                if isinstance(value, list):
                    depth = len(new_key.split('.')) * 2
                    value.append('mcresolverdepth=%s' % depth)
                template_dict[new_key] = value

    for key, value in default_essentials_config.items():
        if isinstance(value, dict) or isinstance(value, OrderedDict):
            # replica_dict[key] = value
            recursive_dictionary_collect(flat_dictionary_template, key, value)
        else:
            if isinstance(value, list):
                value.append('mcresolverdepth=1')  # Hack around the template, and add the depth to prepend to the node
            flat_dictionary_template[key] = value

    template_defaults = OrderedDict()
    node_types = {}

    for key, value in flat_dictionary_template.items():
        node_name = key
        if '.' in key:
            node_split = key.split('.')
            if len(node_split) >= 2:
                node_name = ".".join(node_split[-2:])
            else:
                node_name = node_split[-1]

        node_name = get_name_from_key(node_name)
        # print("Key [%s] Node-Name %s" % (key, node_name))
        # node_name = get_name_from_key(node_name)
        value_type = value.__class__.__name__

        try:
            tdval = type(value)(value)
            if value_type == "bool":
                tdval = str(tdval).lower()

            template_defaults[node_name] = tdval
            node_types[node_name] = value_type
            node_type = tdval.__class__.__name_
        except:
            template_defaults[node_name] = value
            node_types[node_name] = value_type
            node_type = value_type.__class__.__name__
        flat_dictionary_template[key] = "{{{{{node}}}}}".format(node=node_name)

    essentials_template = OrderedDict()

    for key, value in flat_dictionary_template.items():
        # print("Key %s=%s (%s)" % (key, value, type(value).__name__))
        assign_dict_nested_path(essentials_template, key, value)
        # print("%s=%s" % (node_name, value))

    # print(essentials_template)

    # config_yaml = default_essentials_config.dump(default_flow_style=False)
    # print(config_yaml)
    #

    essentials_template_dump = yaml.dump(essentials_template, default_flow_style=False, indent=2, width=1000)

    with open(ess_file, 'r') as essentials_default_data:
        default_essentials_data = essentials_default_data.read()

    essentials_template_dump = restore_yaml_comments(essentials_template_dump, default_essentials_data)

    for node, type in node_types.items():
        if type == "bool" or type == "int" or type == "float":
            essentials_template_dump = essentials_template_dump.replace("'{{{{{node}}}}}'".format(node=node),
                                                                        "{{{{{node}}}}}".format(node=node))
        elif type == "str":
            continue
        elif type == "list":
            item_list = template_defaults[node]
            depth = 2
            list_items = []
            for line in item_list:
                if 'mcresolverdepth' in line:
                    depth = int(line.split('=')[1])
                else:
                    list_items.append(line)

            depth = depth * 2
            item_depth = depth + 2

            # item_list = [line for line in item_list if 'mcresolverdepth' not in line]
            template_defaults[node] = list_items
            loop_statement = "\n{{% for {node}_item in {node} %}}{depth}- {{{{{node}_item}}}}\n{{% endfor %}}"
            loop_statement = loop_statement.format(depth=' ' * depth, node=node)
            essentials_template_dump = essentials_template_dump.replace(" '{{{{{node}}}}}'".format(node=node),
                                                                        loop_statement)

    # print(essentials_template_dump)
    essentials_final_template = ""
    for line in essentials_template_dump.splitlines():
        if 'mcresolverdepth' in line:
            print("Found depth line in %s" % line)
        else:
            essentials_final_template += line + "\n"

    defaults_dump = yaml.dump(template_defaults, default_flow_style=False)
    defaults_dump = defaults_dump.replace(": none", ": ")

    for key, value in template_defaults.items():
        assert key in defaults_dump
        assert key in essentials_template_dump