"""Support for simplified access to data on nodes of type omni.graph.nodes.BundleInspector

This node creates independent outputs containing information about the contents of a bundle attribute. It can be used for
testing or debugging what is inside a bundle as it flows through the graph. The bundle is inspected recursively, so any bundles
inside of the main bundle have their contents added to the output as well. The bundle contents can be printed when the node
evaluates, and it passes the input straight through unchanged so you can insert this node between two nodes to inspect the
data flowing through the graph.
"""

import carb
import numpy

import carb
import omni.graph.core as og
import omni.graph.core._omni_graph_core as _og
import omni.graph.tools.ogn as ogn



class OgnBundleInspectorDatabase(og.Database):
    """Helper class providing simplified access to data on nodes of type omni.graph.nodes.BundleInspector

    Class Members:
        node: Node being evaluated

    Attribute Value Properties:
        Inputs:
            inputs.bundle
            inputs.inspectDepth
            inputs.print
        Outputs:
            outputs.arrayDepths
            outputs.attributeCount
            outputs.bundle
            outputs.childCount
            outputs.count
            outputs.names
            outputs.roles
            outputs.tupleCounts
            outputs.types
            outputs.values
    """

    # Imprint the generator and target ABI versions in the file for JIT generation
    GENERATOR_VERSION = (1, 41, 3)
    TARGET_VERSION = (2, 139, 12)

    # This is an internal object that provides per-class storage of a per-node data dictionary
    PER_NODE_DATA = {}

    # This is an internal object that describes unchanging attributes in a generic way
    # The values in this list are in no particular order, as a per-attribute tuple
    #     Name, Type, ExtendedTypeIndex, UiName, Description, Metadata,
    #     Is_Required, DefaultValue, Is_Deprecated, DeprecationMsg
    # You should not need to access any of this data directly, use the defined database interfaces
    INTERFACE = og.Database._get_interface([
        ('inputs:bundle', 'bundle', 0, 'Bundle To Analyze', 'The attribute bundle to be inspected', {}, True, None, False, ''),
        ('inputs:inspectDepth', 'int', 0, 'Inspect Depth', 'The depth that the inspector is going to traverse and print.\n0 means just attributes on the input bundles. 1 means its immediate children. -1 means infinity.', {ogn.MetadataKeys.DEFAULT: '1'}, True, 1, False, ''),
        ('inputs:print', 'bool', 0, 'Print Contents', 'Setting to true prints the contents of the bundle when the node evaluates', {}, True, False, False, ''),
        ('outputs:arrayDepths', 'int[]', 0, 'Array Depths', 'List of the array depths of attributes present in the bundle', {}, True, None, False, ''),
        ('outputs:attributeCount', 'uint64', 0, 'Attribute Count', 'Number of attributes present in the bundle. Every other output is an array that\nshould have this number of elements in it.', {}, True, None, False, ''),
        ('outputs:bundle', 'bundle', 0, 'Bundle Passthrough', 'The attribute bundle passed through as-is from the input bundle', {}, True, None, False, ''),
        ('outputs:childCount', 'uint64', 0, 'Child Count', 'Number of child bundles present in the bundle.', {}, True, None, False, ''),
        ('outputs:count', 'uint64', 0, 'Attribute Count', 'Number of attributes present in the bundle. Every other output is an array that\nshould have this number of elements in it.', {ogn.MetadataKeys.HIDDEN: 'true'}, True, None, False, ''),
        ('outputs:names', 'token[]', 0, 'Attribute Names', 'List of the names of attributes present in the bundle', {}, True, None, False, ''),
        ('outputs:roles', 'token[]', 0, 'Attribute Roles', 'List of the names of the roles of attributes present in the bundle', {}, True, None, False, ''),
        ('outputs:tupleCounts', 'int[]', 0, 'Tuple Counts', 'List of the tuple counts of attributes present in the bundle', {}, True, None, False, ''),
        ('outputs:types', 'token[]', 0, 'Attribute Base Types', 'List of the types of attributes present in the bundle', {}, True, None, False, ''),
        ('outputs:values', 'token[]', 0, 'Attribute Values', 'List of the bundled attribute values, converted to token format', {}, True, None, False, ''),
    ])

    @classmethod
    def _populate_role_data(cls):
        """Populate a role structure with the non-default roles on this node type"""
        role_data = super()._populate_role_data()
        role_data.inputs.bundle = og.AttributeRole.BUNDLE
        role_data.outputs.bundle = og.AttributeRole.BUNDLE
        return role_data

    class ValuesForInputs(og.DynamicAttributeAccess):
        LOCAL_PROPERTY_NAMES = { }
        """Helper class that creates natural hierarchical access to input attributes"""
        def __init__(self, node: og.Node, attributes, dynamic_attributes: og.DynamicAttributeInterface):
            """Initialize simplified access for the attribute data"""
            context = node.get_graph().get_default_graph_context()
            super().__init__(context, node, attributes, dynamic_attributes)
            self.__bundles = og.BundleContainer(context, node, attributes, [], read_only=True, gpu_ptr_kinds={})
            self._batchedReadAttributes = []
            self._batchedReadValues = []

        @property
        def bundle(self) -> og.BundleContents:
            """Get the bundle wrapper class for the attribute inputs.bundle"""
            return self.__bundles.bundle

        @property
        def inspectDepth(self):
            data_view = og.AttributeValueHelper(self._attributes.inspectDepth)
            return data_view.get()

        @inspectDepth.setter
        def inspectDepth(self, value):
            if self._setting_locked:
                raise og.ReadOnlyError(self._attributes.inspectDepth)
            data_view = og.AttributeValueHelper(self._attributes.inspectDepth)
            data_view.set(value)

        @property
        def print(self):
            data_view = og.AttributeValueHelper(self._attributes.print)
            return data_view.get()

        @print.setter
        def print(self, value):
            if self._setting_locked:
                raise og.ReadOnlyError(self._attributes.print)
            data_view = og.AttributeValueHelper(self._attributes.print)
            data_view.set(value)

        def _prefetch(self):
            readAttributes = self._batchedReadAttributes
            newValues = _og._prefetch_input_attributes_data(readAttributes)
            if len(readAttributes) == len(newValues):
                self._batchedReadValues = newValues

    class ValuesForOutputs(og.DynamicAttributeAccess):
        LOCAL_PROPERTY_NAMES = { }
        """Helper class that creates natural hierarchical access to output attributes"""
        def __init__(self, node: og.Node, attributes, dynamic_attributes: og.DynamicAttributeInterface):
            """Initialize simplified access for the attribute data"""
            context = node.get_graph().get_default_graph_context()
            super().__init__(context, node, attributes, dynamic_attributes)
            self.__bundles = og.BundleContainer(context, node, attributes, [], read_only=False, gpu_ptr_kinds={})
            self.arrayDepths_size = None
            self.names_size = None
            self.roles_size = None
            self.tupleCounts_size = None
            self.types_size = None
            self.values_size = None
            self._batchedWriteValues = { }

        @property
        def arrayDepths(self):
            data_view = og.AttributeValueHelper(self._attributes.arrayDepths)
            return data_view.get(reserved_element_count=self.arrayDepths_size)

        @arrayDepths.setter
        def arrayDepths(self, value):
            data_view = og.AttributeValueHelper(self._attributes.arrayDepths)
            data_view.set(value)
            self.arrayDepths_size = data_view.get_array_size()

        @property
        def attributeCount(self):
            data_view = og.AttributeValueHelper(self._attributes.attributeCount)
            return data_view.get()

        @attributeCount.setter
        def attributeCount(self, value):
            data_view = og.AttributeValueHelper(self._attributes.attributeCount)
            data_view.set(value)

        @property
        def bundle(self) -> og.BundleContents:
            """Get the bundle wrapper class for the attribute outputs.bundle"""
            return self.__bundles.bundle

        @bundle.setter
        def bundle(self, bundle: og.BundleContents):
            """Overwrite the bundle attribute outputs.bundle with a new bundle"""
            if not isinstance(bundle, og.BundleContents):
                carb.log_error("Only bundle attributes can be assigned to another bundle attribute")
            self.__bundles.bundle.bundle = bundle

        @property
        def childCount(self):
            data_view = og.AttributeValueHelper(self._attributes.childCount)
            return data_view.get()

        @childCount.setter
        def childCount(self, value):
            data_view = og.AttributeValueHelper(self._attributes.childCount)
            data_view.set(value)

        @property
        def count(self):
            data_view = og.AttributeValueHelper(self._attributes.count)
            return data_view.get()

        @count.setter
        def count(self, value):
            data_view = og.AttributeValueHelper(self._attributes.count)
            data_view.set(value)

        @property
        def names(self):
            data_view = og.AttributeValueHelper(self._attributes.names)
            return data_view.get(reserved_element_count=self.names_size)

        @names.setter
        def names(self, value):
            data_view = og.AttributeValueHelper(self._attributes.names)
            data_view.set(value)
            self.names_size = data_view.get_array_size()

        @property
        def roles(self):
            data_view = og.AttributeValueHelper(self._attributes.roles)
            return data_view.get(reserved_element_count=self.roles_size)

        @roles.setter
        def roles(self, value):
            data_view = og.AttributeValueHelper(self._attributes.roles)
            data_view.set(value)
            self.roles_size = data_view.get_array_size()

        @property
        def tupleCounts(self):
            data_view = og.AttributeValueHelper(self._attributes.tupleCounts)
            return data_view.get(reserved_element_count=self.tupleCounts_size)

        @tupleCounts.setter
        def tupleCounts(self, value):
            data_view = og.AttributeValueHelper(self._attributes.tupleCounts)
            data_view.set(value)
            self.tupleCounts_size = data_view.get_array_size()

        @property
        def types(self):
            data_view = og.AttributeValueHelper(self._attributes.types)
            return data_view.get(reserved_element_count=self.types_size)

        @types.setter
        def types(self, value):
            data_view = og.AttributeValueHelper(self._attributes.types)
            data_view.set(value)
            self.types_size = data_view.get_array_size()

        @property
        def values(self):
            data_view = og.AttributeValueHelper(self._attributes.values)
            return data_view.get(reserved_element_count=self.values_size)

        @values.setter
        def values(self, value):
            data_view = og.AttributeValueHelper(self._attributes.values)
            data_view.set(value)
            self.values_size = data_view.get_array_size()

        def _commit(self):
            _og._commit_output_attributes_data(self._batchedWriteValues)
            self._batchedWriteValues = { }

    class ValuesForState(og.DynamicAttributeAccess):
        """Helper class that creates natural hierarchical access to state attributes"""
        def __init__(self, node: og.Node, attributes, dynamic_attributes: og.DynamicAttributeInterface):
            """Initialize simplified access for the attribute data"""
            context = node.get_graph().get_default_graph_context()
            super().__init__(context, node, attributes, dynamic_attributes)

    def __init__(self, node):
        super().__init__(node)
        dynamic_attributes = self.dynamic_attribute_data(node, og.AttributePortType.ATTRIBUTE_PORT_TYPE_INPUT)
        self.inputs = OgnBundleInspectorDatabase.ValuesForInputs(node, self.attributes.inputs, dynamic_attributes)
        dynamic_attributes = self.dynamic_attribute_data(node, og.AttributePortType.ATTRIBUTE_PORT_TYPE_OUTPUT)
        self.outputs = OgnBundleInspectorDatabase.ValuesForOutputs(node, self.attributes.outputs, dynamic_attributes)
        dynamic_attributes = self.dynamic_attribute_data(node, og.AttributePortType.ATTRIBUTE_PORT_TYPE_STATE)
        self.state = OgnBundleInspectorDatabase.ValuesForState(node, self.attributes.state, dynamic_attributes)
