# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from typing import Any, Dict, Optional

from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer


class ContainerNode:
    """A node in the container tree. It represents one container.

    The container it represents is referenced by its container_id. During normal use of the tree, this container is
    not constructed. Only when parts of the tree need to get loaded in the container stack should it get constructed.
    """

    def __init__(self, container_id: str) -> None:
        """Creates a new node for the container tree.

        :param container_id: The ID of the container that this node should represent.
        """

        self.container_id = container_id
        self._container = None  # type: Optional[InstanceContainer]
        self.children_map = {}  # type: Dict[str, ContainerNode]  # Mapping from container ID to container node.

    def getMetadata(self) -> Dict[str, Any]:
        """Gets the metadata of the container that this node represents.

        Getting the metadata from the container directly is about 10x as fast.

        :return: The metadata of the container in this node.
        """

        return ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id)[0]

    def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
        """Get an entry from the metadata of the container that this node contains.

        This is just a convenience function.

        :param entry: The metadata entry key to return.
        :param default: If the metadata is not present or the container is not found, the value of this default is
        returned.

        :return: The value of the metadata entry, or the default if it was not present.
        """

        container_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id)
        if len(container_metadata) == 0:
            return default
        return container_metadata[0].get(entry, default)

    @property
    def container(self) -> Optional[InstanceContainer]:
        """The container that this node's container ID refers to.

        This can be used to finally instantiate the container in order to put it in the container stack.

        :return: A container.
        """

        if not self._container:
            container_list = ContainerRegistry.getInstance().findInstanceContainers(id = self.container_id)
            if len(container_list) == 0:
                Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = self.container_id))
                error_message = ConfigurationErrorMessage.getInstance()
                error_message.addFaultyContainers(self.container_id)
                return None
            self._container = container_list[0]
        return self._container

    def __str__(self) -> str:
        return "%s[%s]" % (self.__class__.__name__, self.container_id)