"""
Contains the support class for managing attributes whose data is 32 bit integers
"""
from typing import Dict, List

from ..utils import _EXTENDED_TYPE_UNION, ParseError, to_usd_docs
from .AnyAttributeManager import AnyAttributeManager
from .AttributeManager import AttributeManager


class UnionAttributeManager(AnyAttributeManager):
    """Support class for attributes of type union

    The union attribute type is different from all of the others in that it doesn't have a fixed data type, it is
    merely a placeholder for an attribute that accepts a variety of data types. It can be thought of as an "any"
    attribute with restrictions on exactly what kind of connections can be made. (e.g. if the union accepts float
    and double then it cannot accept a connection from a string attribute)

    Members:
        __types_accepted: Dictionary of {"typeName": AttributeManager} for all of the data types that this union
                          attribute can accept.
    """

    # This is just a keyword for the attribute manager, this doesn't appear in the type name directly
    OGN_TYPE = "union"

    def __init__(self, attribute_name: str, attribute_type_name: str, types_accepted: Dict[str, AttributeManager]):
        """Set up the empty attribute values for population from the JSON description

        Args:
            attribute_name: Unique name for this attribute
            attribute_type_name: Unique name for this attribute type
            types_accepted: Same type as member __types_accepted

        Raises:
            ParseError: If any of the accepted types are not legal
        """
        super().__init__(attribute_name, attribute_type_name)
        self.__types_accepted = types_accepted

    def cpp_extended_type(self):
        """Returns the extended type identifier for C++ types"""
        return "kExtendedAttributeType_Union"

    def ogn_type(self) -> str:
        """Returns a string containing the fully expanded name of this attribute type in a .ogn file"""
        type_information = sorted(self.__types_accepted.keys())
        if self.array_depth == 1:
            type_information = [type_information]
        return f"{type_information}"

    def usd_type_name(self) -> str:
        """Returns a string with the data type the attribute would use in a USD file"""
        return ",".join(sorted(self.__types_accepted.keys()))

    def python_extended_type(self):
        """Returns the extended type identifier and descriptor for Python attribute types"""
        return (_EXTENDED_TYPE_UNION, ",".join(sorted(self.__types_accepted.keys())))

    def usd_type_accepted_description(self) -> str:
        """Returns a string that will be the default value of the USD token, describing accepted types"""
        return f"union of {','.join(self.__types_accepted.keys())}"

    def emit_usd_declaration(self, out) -> List[str]:
        """Print a declaration for this attribute in USD

        Args:
            out: Output handler where the USD will be emitted
        """
        try:
            usd_name = self.usd_name()
            # Overriding default behavior of using self.usd_type_name()
            usd_type = "token"
        except ParseError:
            # Attributes without USD representations can be skipped
            return

        docs = to_usd_docs(self.description)

        default_value = self.usd_default_value()
        if out.indent(f"custom {usd_type} {usd_name}{default_value} ("):
            out.write(docs)
            out.exdent(")")
