import logging

from galaxy import util
from galaxy.tool_shed.util.container_util import (
    generate_repository_dependencies_key_for_repository,
    get_components_from_key,
)
from galaxy.tool_shed.util.repository_util import (
    extract_components_from_tuple,
    get_tool_shed_repository_by_id,
)
from galaxy.util import UNKNOWN
from galaxy.util.tool_shed.common_util import parse_repository_dependency_tuple

log = logging.getLogger(__name__)


class Folder:
    """Container object."""

    def __init__(self, id=None, key=None, label=None, parent=None):
        self.id = id
        self.key = key
        self.label = label
        self.parent = parent
        self.current_repository_installation_errors = []
        self.current_repository_successful_installations = []
        self.description = None
        self.datatypes = []
        self.folders = []
        self.invalid_data_managers = []
        self.invalid_repository_dependencies = []
        self.invalid_tool_dependencies = []
        self.invalid_tools = []
        self.missing_test_components = []
        self.readme_files = []
        self.repository_dependencies = []
        self.repository_installation_errors = []
        self.repository_successful_installations = []
        self.test_environments = []
        self.tool_dependencies = []
        self.valid_tools = []
        self.valid_data_managers = []
        self.workflows = []

    def contains_folder(self, folder):
        for index, contained_folder in enumerate(self.folders):
            if folder == contained_folder:
                return index, contained_folder
        return 0, None

    def contains_repository_dependency(self, repository_dependency):
        listified_repository_dependency = repository_dependency.listify
        for contained_repository_dependency in self.repository_dependencies:
            if contained_repository_dependency.listify == listified_repository_dependency:
                return True
        return False

    def remove_repository_dependency(self, repository_dependency):
        listified_repository_dependency = repository_dependency.listify
        self.repository_dependencies = [
            contained_repository_dependency
            for contained_repository_dependency in self.repository_dependencies
            if contained_repository_dependency.listify != listified_repository_dependency
        ]

    def to_dict(self):
        folders = []
        if self.folders:
            for folder in self.folders:
                folders.append(folder.to_dict())
        repository_dependencies = []
        for rd in self.repository_dependencies or []:
            repository_dependencies.append(rd.to_dict())
        return {
            "description": self.description,
            "folders": folders,
            "repository_dependencies": repository_dependencies,
        }


class DataManager:
    """Data Manager object"""

    def __init__(self, id=None, name=None, version=None, data_tables=None):
        self.id = id
        self.name = name
        self.version = version
        self.data_tables = data_tables


class InvalidDataManager:
    """Invalid data Manager object"""

    def __init__(self, id=None, index=None, error=None):
        self.id = id
        self.index = index
        self.error = error


class InvalidTool:
    """Invalid tool object"""

    def __init__(
        self,
        id=None,
        tool_config=None,
        repository_id=None,
        changeset_revision=None,
        repository_installation_status=None,
    ):
        self.id = id
        self.tool_config = tool_config
        self.repository_id = repository_id
        self.changeset_revision = changeset_revision
        self.repository_installation_status = repository_installation_status


class ReadMe:
    """Readme text object"""

    def __init__(self, id=None, name=None, text=None):
        self.id = id
        self.name = name
        self.text = text


class RepositoryDependency:
    """Repository dependency object"""

    def __init__(
        self,
        id=None,
        toolshed=None,
        repository_name=None,
        repository_owner=None,
        changeset_revision=None,
        prior_installation_required=False,
        only_if_compiling_contained_td=False,
        installation_status=None,
        tool_shed_repository_id=None,
    ):
        self.id = id
        self.toolshed = toolshed
        self.repository_name = repository_name
        self.repository_owner = repository_owner
        self.changeset_revision = changeset_revision
        self.prior_installation_required = prior_installation_required
        self.only_if_compiling_contained_td = only_if_compiling_contained_td
        self.installation_status = installation_status
        self.tool_shed_repository_id = tool_shed_repository_id

    @property
    def listify(self):
        return [
            self.toolshed,
            self.repository_name,
            self.repository_owner,
            self.changeset_revision,
            self.prior_installation_required,
            self.only_if_compiling_contained_td,
        ]

    def to_dict(self):
        return {
            "toolshed": self.toolshed,
            "repository_name": self.repository_name,
            "repository_owner": self.repository_owner,
            "changeset_revision": self.changeset_revision,
        }


class Tool:
    """Tool object"""

    def __init__(
        self,
        id=None,
        tool_config=None,
        tool_id=None,
        name=None,
        description=None,
        version=None,
        profile=None,
        requirements=None,
        repository_id=None,
        changeset_revision=None,
        repository_installation_status=None,
    ):
        self.id = id
        self.tool_config = tool_config
        self.tool_id = tool_id
        self.name = name
        self.description = description
        self.version = version
        self.profile = profile
        self.requirements = requirements
        self.repository_id = repository_id
        self.changeset_revision = changeset_revision
        self.repository_installation_status = repository_installation_status


class ToolDependency:
    """Tool dependency object"""

    def __init__(
        self,
        id=None,
        name=None,
        version=None,
        type=None,
        readme=None,
        installation_status=None,
        repository_id=None,
        tool_dependency_id=None,
    ):
        self.id = id
        self.name = name
        self.version = version
        self.type = type
        self.readme = readme
        self.installation_status = installation_status
        self.repository_id = repository_id
        self.tool_dependency_id = tool_dependency_id

    @property
    def listify(self):
        return [self.name, self.version, self.type]


class UtilityContainerManager:
    def __init__(self, app):
        self.app = app

    def build_data_managers_folder(self, folder_id, data_managers, label=None):
        """Return a folder hierarchy containing Data Managers."""
        if data_managers:
            if label is None:
                label = "Data Managers"
            data_manager_id = 0
            folder_id += 1
            data_managers_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            key = "valid_data_managers"
            folder = Folder(id=folder_id, key=key, label=label, parent=data_managers_root_folder)
            data_managers_root_folder.folders.append(folder)
            # Insert a header row.
            data_manager_id += 1
            data_manager = DataManager(id=data_manager_id, name="Name", version="Version", data_tables="Data Tables")
            folder.valid_data_managers.append(data_manager)
            for data_manager_dict in data_managers.values():
                data_manager_id += 1
                try:
                    name = data_manager_dict.get("name", "")
                    version = data_manager_dict.get("version", "")
                    data_tables = ", ".join(data_manager_dict.get("data_tables", ""))
                except Exception as e:
                    name = str(e)
                    version = UNKNOWN
                    data_tables = UNKNOWN
                data_manager = DataManager(id=data_manager_id, name=name, version=version, data_tables=data_tables)
                folder.valid_data_managers.append(data_manager)
        else:
            data_managers_root_folder = None
        return folder_id, data_managers_root_folder

    def build_invalid_data_managers_folder(self, folder_id, data_managers, error_messages=None, label=None):
        """Return a folder hierarchy containing invalid Data Managers."""
        if data_managers or error_messages:
            if label is None:
                label = "Invalid Data Managers"
            data_manager_id = 0
            folder_id += 1
            data_managers_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            key = "invalid_data_managers"
            folder = Folder(id=folder_id, key=key, label=label, parent=data_managers_root_folder)
            data_managers_root_folder.folders.append(folder)
            # Insert a header row.
            data_manager_id += 1
            data_manager = InvalidDataManager(id=data_manager_id, index="Element Index", error="Error")
            folder.invalid_data_managers.append(data_manager)
            if error_messages:
                for error_message in error_messages:
                    data_manager_id += 1
                    data_manager = InvalidDataManager(id=data_manager_id, index=0, error=error_message)
                    folder.invalid_data_managers.append(data_manager)
            for data_manager_dict in data_managers:
                data_manager_id += 1
                data_manager = InvalidDataManager(
                    id=data_manager_id,
                    index=data_manager_dict.get("index", 0) + 1,
                    error=data_manager_dict.get("error_message", ""),
                )
                folder.invalid_data_managers.append(data_manager)
        else:
            data_managers_root_folder = None
        return folder_id, data_managers_root_folder

    def build_invalid_tools_folder(
        self, folder_id, invalid_tool_configs, changeset_revision, repository=None, label="Invalid tools"
    ):
        """Return a folder hierarchy containing invalid tools."""
        # TODO: Should we display invalid tools on the tool panel selection page when installing the
        # repository into Galaxy?
        if invalid_tool_configs:
            invalid_tool_id = 0
            folder_id += 1
            invalid_tools_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            folder = Folder(id=folder_id, key="invalid_tools", label=label, parent=invalid_tools_root_folder)
            invalid_tools_root_folder.folders.append(folder)
            for invalid_tool_config in invalid_tool_configs:
                invalid_tool_id += 1
                if repository:
                    repository_id = repository.id
                    if self.app.name == "galaxy":
                        repository_installation_status = repository.status
                    else:
                        repository_installation_status = None
                else:
                    repository_id = None
                    repository_installation_status = None
                invalid_tool = InvalidTool(
                    id=invalid_tool_id,
                    tool_config=invalid_tool_config,
                    repository_id=repository_id,
                    changeset_revision=changeset_revision,
                    repository_installation_status=repository_installation_status,
                )
                folder.invalid_tools.append(invalid_tool)
        else:
            invalid_tools_root_folder = None
        return folder_id, invalid_tools_root_folder

    def build_readme_files_folder(self, folder_id, readme_files_dict, label="Readme files"):
        """Return a folder hierarchy containing readme text files."""
        if readme_files_dict:
            readme_id = 0
            folder_id += 1
            readme_files_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            readme_files_folder = Folder(id=folder_id, key="readme_files", label=label, parent=readme_files_root_folder)
            multiple_readme_files = len(readme_files_dict) > 1
            readme_files_root_folder.folders.append(readme_files_folder)
            for readme_file_name, readme_file_text in readme_files_dict.items():
                readme_id += 1
                readme = ReadMe(id=readme_id, name=readme_file_name, text=readme_file_text)
                if multiple_readme_files:
                    folder_id += 1
                    folder = Folder(
                        id=folder_id, key=readme_file_name, label=readme_file_name, parent=readme_files_folder
                    )
                    folder.readme_files.append(readme)
                    readme_files_folder.folders.append(folder)
                else:
                    readme_files_folder.readme_files.append(readme)
        else:
            readme_files_root_folder = None
        return folder_id, readme_files_root_folder

    def build_repository_dependencies_folder(
        self, folder_id, repository_dependencies, label="Repository dependencies", installed=False
    ):
        """Return a folder hierarchy containing repository dependencies."""
        if repository_dependencies:
            repository_dependency_id = 0
            folder_id += 1
            # Create the root folder.
            repository_dependencies_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            # Create the Repository dependencies folder and add it to the root folder.
            repository_dependencies_folder_key = repository_dependencies["root_key"]
            repository_dependencies_folder = Folder(
                id=folder_id,
                key=repository_dependencies_folder_key,
                label=label,
                parent=repository_dependencies_root_folder,
            )
            del repository_dependencies["root_key"]
            # The received repository_dependencies is a dictionary with keys: 'root_key', 'description', and one or more
            # repository_dependency keys.  We want the description value associated with the repository_dependencies_folder.
            repository_dependencies_folder.description = repository_dependencies.get("description", None)
            repository_dependencies_root_folder.folders.append(repository_dependencies_folder)
            del repository_dependencies["description"]
            (
                repository_dependencies_folder,
                folder_id,
                repository_dependency_id,
            ) = self.populate_repository_dependencies_container(
                repository_dependencies_folder, repository_dependencies, folder_id, repository_dependency_id
            )
            repository_dependencies_folder = self.prune_repository_dependencies(repository_dependencies_folder)
        else:
            repository_dependencies_root_folder = None
        return folder_id, repository_dependencies_root_folder

    def build_tools_folder(
        self, folder_id, tool_dicts, repository, changeset_revision, valid=True, label="Valid tools"
    ):
        """Return a folder hierarchy containing valid tools."""
        if tool_dicts:
            container_object_tool_id = 0
            folder_id += 1
            tools_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            folder = Folder(id=folder_id, key="tools", label=label, parent=tools_root_folder)
            if self.app.name == "galaxy":
                folder.description = "click the name to inspect the tool metadata"
            tools_root_folder.folders.append(folder)
            # Insert a header row.
            container_object_tool_id += 1
            tool = Tool(
                id=container_object_tool_id,
                tool_config="",
                tool_id="",
                name="Name",
                description="Description",
                version="Version",
                profile="Minimum Galaxy Version",
                requirements="",
                repository_id="",
                changeset_revision="",
            )
            folder.valid_tools.append(tool)
            if repository:
                repository_id = repository.id
                if self.app.name == "galaxy":
                    repository_installation_status = repository.status
                else:
                    repository_installation_status = None
            else:
                repository_id = None
                repository_installation_status = None
            for tool_dict in tool_dicts:
                if not isinstance(tool_dict, dict):
                    # Due to some previous bug (hopefully not current) invalid tool strings may be included in the received
                    # list of tool_dicts.  For example, the picard repository metadata has 2 invalid tools in the received
                    # list of supposedly valid tools:  'rgPicardASMetrics.xml', 'rgPicardGCBiasMetrics.xml'.
                    continue
                container_object_tool_id += 1
                requirements = tool_dict.get("requirements", None)
                if requirements is not None:
                    # 'requirements': [{'version': '1.56.0', 'type': 'package', 'name': 'picard'}],
                    requirements_str = ""
                    for requirement_dict in requirements:
                        try:
                            requirement_name = str(requirement_dict.get("name", UNKNOWN))
                            requirement_type = str(requirement_dict.get("type", UNKNOWN))
                        except Exception as e:
                            requirement_name = str(e)
                            requirement_type = UNKNOWN
                        requirements_str += f"{requirement_name} ({requirement_type}), "
                    requirements_str = requirements_str.rstrip(", ")
                else:
                    requirements_str = "none"
                try:
                    tool_config = str(tool_dict.get("tool_config", "missing"))
                    tool_id = str(tool_dict.get("id", UNKNOWN))
                    name = str(tool_dict.get("name", UNKNOWN))
                    description = str(tool_dict.get("description", ""))
                    version = str(tool_dict.get("version", UNKNOWN))
                    profile = str(tool_dict.get("profile", "any"))
                except Exception as e:
                    tool_config = str(e)
                    tool_id = UNKNOWN
                    name = UNKNOWN
                    description = ""
                    version = UNKNOWN
                tool = Tool(
                    id=container_object_tool_id,
                    tool_config=tool_config,
                    tool_id=tool_id,
                    name=name,
                    description=description,
                    version=version,
                    profile=profile,
                    requirements=requirements_str,
                    repository_id=repository_id,
                    changeset_revision=changeset_revision,
                    repository_installation_status=repository_installation_status,
                )
                folder.valid_tools.append(tool)
        else:
            tools_root_folder = None
        return folder_id, tools_root_folder

    def build_tool_dependencies_folder(
        self,
        folder_id,
        tool_dependencies,
        label="Tool dependencies",
        missing=False,
        new_install=False,
        reinstalling=False,
    ):
        """Return a folder hierarchy containing tool dependencies."""
        # When we're in Galaxy (not the tool shed) and the tool dependencies are not installed or are in an error state,
        # they are considered missing.  The tool dependency status will be displayed only if a record exists for the tool
        # dependency in the Galaxy database, but the tool dependency is not installed.  The value for new_install will be
        # True only if the associated repository in being installed for the first time.  This value is used in setting the
        # container description.
        if tool_dependencies:
            tool_dependency_id = 0
            folder_id += 1
            tool_dependencies_root_folder = Folder(id=folder_id, key="root", label="root", parent=None)
            folder_id += 1
            folder = Folder(id=folder_id, key="tool_dependencies", label=label, parent=tool_dependencies_root_folder)
            if self.app.name == "galaxy":
                if new_install or reinstalling:
                    folder.description = "repository tools require handling of these dependencies"
                elif missing and not new_install and not reinstalling:
                    folder.description = "click the name to install the missing dependency"
                else:
                    folder.description = "click the name to browse the dependency installation directory"
            tool_dependencies_root_folder.folders.append(folder)
            # Insert a header row.
            tool_dependency_id += 1
            if self.app.name == "galaxy":
                tool_dependency = ToolDependency(
                    id=tool_dependency_id,
                    name="Name",
                    version="Version",
                    type="Type",
                    readme=None,
                    installation_status="Installation status",
                    repository_id=None,
                    tool_dependency_id=None,
                )
            else:
                tool_dependency = ToolDependency(
                    id=tool_dependency_id,
                    name="Name",
                    version="Version",
                    type="Type",
                    readme=None,
                    installation_status=None,
                    repository_id=None,
                    tool_dependency_id=None,
                )
            folder.tool_dependencies.append(tool_dependency)
            for dependency_key, requirements_dict in tool_dependencies.items():
                tool_dependency_id += 1
                if dependency_key in ["set_environment"]:
                    for set_environment_dict in requirements_dict:
                        try:
                            name = set_environment_dict.get("name", None)
                            type = set_environment_dict["type"]
                            repository_id = set_environment_dict.get("repository_id", None)
                            td_id = set_environment_dict.get("tool_dependency_id", None)
                        except Exception as e:
                            name = str(e)
                            type = UNKNOWN
                            repository_id = UNKNOWN
                            td_id = UNKNOWN
                        if self.app.name == "galaxy":
                            try:
                                installation_status = set_environment_dict.get("status", "Never installed")
                            except Exception as e:
                                installation_status = str(e)
                        else:
                            installation_status = None
                        tool_dependency = ToolDependency(
                            id=tool_dependency_id,
                            name=name,
                            version=None,
                            type=type,
                            readme=None,
                            installation_status=installation_status,
                            repository_id=repository_id,
                            tool_dependency_id=td_id,
                        )
                        folder.tool_dependencies.append(tool_dependency)
                else:
                    try:
                        name = requirements_dict["name"]
                        version = requirements_dict["version"]
                        type = requirements_dict["type"]
                        repository_id = requirements_dict.get("repository_id", None)
                        td_id = requirements_dict.get("tool_dependency_id", None)
                    except Exception as e:
                        name = str(e)
                        version = UNKNOWN
                        type = UNKNOWN
                        repository_id = UNKNOWN
                        td_id = UNKNOWN
                    if self.app.name == "galaxy":
                        try:
                            installation_status = requirements_dict.get("status", "Never installed")
                        except Exception as e:
                            installation_status = str(e)
                    else:
                        installation_status = None
                    tool_dependency = ToolDependency(
                        id=tool_dependency_id,
                        name=name,
                        version=version,
                        type=type,
                        readme=None,
                        installation_status=installation_status,
                        repository_id=repository_id,
                        tool_dependency_id=td_id,
                    )
                    folder.tool_dependencies.append(tool_dependency)
        else:
            tool_dependencies_root_folder = None
        return folder_id, tool_dependencies_root_folder

    def generate_repository_dependencies_folder_label_from_key(
        self,
        repository_name,
        repository_owner,
        changeset_revision,
        prior_installation_required,
        only_if_compiling_contained_td,
        key,
    ):
        """Return a repository dependency label based on the repository dependency key."""
        if self.key_is_current_repositorys_key(
            repository_name,
            repository_owner,
            changeset_revision,
            prior_installation_required,
            only_if_compiling_contained_td,
            key,
        ):
            label = "Repository dependencies"
        else:
            if util.asbool(prior_installation_required):
                prior_installation_required_str = " <i>(prior install required)</i>"
            else:
                prior_installation_required_str = ""
            label = f"Repository <b>{repository_name}</b> revision <b>{changeset_revision}</b> owned by <b>{repository_owner}</b>{prior_installation_required_str}"
        return label

    def get_components_from_repository_dependency_for_installed_repository(self, repository_dependency):
        """
        Parse a repository dependency and return components necessary for proper display
        in Galaxy on the Manage repository page.
        """
        # Default prior_installation_required and only_if_compiling_contained_td to False.
        prior_installation_required = "False"
        only_if_compiling_contained_td = "False"
        if len(repository_dependency) == 6:
            # Metadata should have been reset on this installed repository, but it wasn't.
            tool_shed_repository_id = repository_dependency[4]
            installation_status = repository_dependency[5]
            tool_shed, name, owner, changeset_revision = repository_dependency[0:4]
            repository_dependency = [
                tool_shed,
                name,
                owner,
                changeset_revision,
                prior_installation_required,
                only_if_compiling_contained_td,
            ]
        elif len(repository_dependency) == 7:
            # We have a repository dependency tuple that includes a prior_installation_required value but not a only_if_compiling_contained_td value.
            tool_shed_repository_id = repository_dependency[5]
            installation_status = repository_dependency[6]
            tool_shed, name, owner, changeset_revision, prior_installation_required = repository_dependency[0:5]
            repository_dependency = [
                tool_shed,
                name,
                owner,
                changeset_revision,
                prior_installation_required,
                only_if_compiling_contained_td,
            ]
        elif len(repository_dependency) == 8:
            # We have a repository dependency tuple that includes both a prior_installation_required value
            # and a only_if_compiling_contained_td value.
            tool_shed_repository_id = repository_dependency[6]
            installation_status = repository_dependency[7]
            repository_dependency = repository_dependency[0:6]
        else:
            tool_shed_repository_id = None
            installation_status = UNKNOWN
        if tool_shed_repository_id:
            tool_shed_repository = get_tool_shed_repository_by_id(
                self.app, self.app.security.encode_id(tool_shed_repository_id)
            )
            if tool_shed_repository:
                if tool_shed_repository.missing_repository_dependencies:
                    installation_status = f"{installation_status}, missing repository dependencies"
                elif tool_shed_repository.missing_tool_dependencies:
                    installation_status = f"{installation_status}, missing tool dependencies"
        return tool_shed_repository_id, installation_status, repository_dependency

    def get_folder(self, folder, key):
        if folder.key == key:
            return folder
        for sub_folder in folder.folders:
            return self.get_folder(sub_folder, key)
        return None

    def handle_repository_dependencies_container_entry(
        self, repository_dependencies_folder, rd_key, rd_value, folder_id, repository_dependency_id, folder_keys
    ):
        repository_components_tuple = get_components_from_key(rd_key)
        components_list = extract_components_from_tuple(repository_components_tuple)
        toolshed, repository_name, repository_owner, changeset_revision = components_list[0:4]
        # For backward compatibility to the 12/20/12 Galaxy release.
        if len(components_list) == 4:
            prior_installation_required = "False"
            only_if_compiling_contained_td = "False"
        elif len(components_list) == 5:
            prior_installation_required = components_list[4]
            only_if_compiling_contained_td = "False"
        elif len(components_list) == 6:
            prior_installation_required = components_list[4]
            only_if_compiling_contained_td = components_list[5]
        folder = self.get_folder(repository_dependencies_folder, rd_key)
        label = self.generate_repository_dependencies_folder_label_from_key(
            repository_name,
            repository_owner,
            changeset_revision,
            prior_installation_required,
            only_if_compiling_contained_td,
            repository_dependencies_folder.key,
        )
        if folder:
            if rd_key not in folder_keys:
                folder_id += 1
                sub_folder = Folder(id=folder_id, key=rd_key, label=label, parent=folder)
                folder.folders.append(sub_folder)
            else:
                sub_folder = folder
        else:
            folder_id += 1
            sub_folder = Folder(id=folder_id, key=rd_key, label=label, parent=repository_dependencies_folder)
            repository_dependencies_folder.folders.append(sub_folder)
        if self.app.name == "galaxy":
            # Insert a header row.
            repository_dependency_id += 1
            repository_dependency = RepositoryDependency(
                id=repository_dependency_id,
                repository_name="Name",
                changeset_revision="Revision",
                repository_owner="Owner",
                installation_status="Installation status",
            )
            # Insert the header row into the folder.
            sub_folder.repository_dependencies.append(repository_dependency)
        for repository_dependency in rd_value:
            if self.app.name == "galaxy":
                (
                    tool_shed_repository_id,
                    installation_status,
                    repository_dependency,
                ) = self.get_components_from_repository_dependency_for_installed_repository(repository_dependency)
            else:
                tool_shed_repository_id = None
                installation_status = None
            can_create_dependency = not self.is_subfolder_of(sub_folder, repository_dependency)
            if can_create_dependency:
                (
                    toolshed,
                    repository_name,
                    repository_owner,
                    changeset_revision,
                    prior_installation_required,
                    only_if_compiling_contained_td,
                ) = parse_repository_dependency_tuple(repository_dependency)
                repository_dependency_id += 1
                repository_dependency = RepositoryDependency(
                    id=repository_dependency_id,
                    toolshed=toolshed,
                    repository_name=repository_name,
                    repository_owner=repository_owner,
                    changeset_revision=changeset_revision,
                    prior_installation_required=util.asbool(prior_installation_required),
                    only_if_compiling_contained_td=util.asbool(only_if_compiling_contained_td),
                    installation_status=installation_status,
                    tool_shed_repository_id=tool_shed_repository_id,
                )
                # Insert the repository_dependency into the folder.
                sub_folder.repository_dependencies.append(repository_dependency)
        return repository_dependencies_folder, folder_id, repository_dependency_id

    def is_subfolder_of(self, folder, repository_dependency):
        (
            toolshed,
            repository_name,
            repository_owner,
            changeset_revision,
            prior_installation_required,
            only_if_compiling_contained_td,
        ) = parse_repository_dependency_tuple(repository_dependency)
        key = generate_repository_dependencies_key_for_repository(
            toolshed,
            repository_name,
            repository_owner,
            changeset_revision,
            prior_installation_required,
            only_if_compiling_contained_td,
        )
        for sub_folder in folder.folders:
            if key == sub_folder.key:
                return True
        return False

    def key_is_current_repositorys_key(
        self,
        repository_name,
        repository_owner,
        changeset_revision,
        prior_installation_required,
        only_if_compiling_contained_td,
        key,
    ):
        repository_components_tuple = get_components_from_key(key)
        components_list = extract_components_from_tuple(repository_components_tuple)
        toolshed, key_name, key_owner, key_changeset_revision = components_list[0:4]
        # For backward compatibility to the 12/20/12 Galaxy release.
        if len(components_list) == 4:
            key_prior_installation_required = "False"
            key_only_if_compiling_contained_td = "False"
        elif len(components_list) == 5:
            key_prior_installation_required = components_list[4]
            key_only_if_compiling_contained_td = "False"
        elif len(components_list) == 6:
            key_prior_installation_required = components_list[4]
            key_only_if_compiling_contained_td = components_list[5]
        if (
            repository_name == key_name
            and repository_owner == key_owner
            and changeset_revision == key_changeset_revision
            and prior_installation_required == key_prior_installation_required
            and only_if_compiling_contained_td == key_only_if_compiling_contained_td
        ):
            return True
        return False

    def populate_repository_dependencies_container(
        self, repository_dependencies_folder, repository_dependencies, folder_id, repository_dependency_id
    ):
        folder_keys = []
        for key in repository_dependencies.keys():
            if key not in folder_keys:
                folder_keys.append(key)
        for key, value in repository_dependencies.items():
            (
                repository_dependencies_folder,
                folder_id,
                repository_dependency_id,
            ) = self.handle_repository_dependencies_container_entry(
                repository_dependencies_folder, key, value, folder_id, repository_dependency_id, folder_keys
            )
        return repository_dependencies_folder, folder_id, repository_dependency_id

    def prune_folder(self, folder, repository_dependency):
        listified_repository_dependency = repository_dependency.listify
        if self.is_subfolder_of(folder, listified_repository_dependency):
            folder.repository_dependencies.remove(repository_dependency)

    def prune_repository_dependencies(self, folder):
        """
        Since the object used to generate a repository dependencies container is a dictionary
        and not an OrderedDict() (it must be json-serialize-able), the order in which the dictionary
        is processed to create the container sometimes results in repository dependency entries
        in a folder that also includes the repository dependency as a sub-folder (if the repository
        dependency has its own repository dependency).  This method will remove all repository
        dependencies from folder that are also sub-folders of folder.
        """
        repository_dependencies = list(folder.repository_dependencies)
        for repository_dependency in repository_dependencies:
            self.prune_folder(folder, repository_dependency)
        for sub_folder in folder.folders:
            return self.prune_repository_dependencies(sub_folder)
        return folder
