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

This is a tutorial node. It illustrates how to access data whose memory location, CPU or GPU, is  determined at runtime in
the compute method. The data types are the same as for the purely CPU and  purely GPU tutorials, it is only the access method
that changes. The input 'is_gpu' determines where  the data of the other attributes can be accessed.
"""

import carb
import numpy

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



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

    Class Members:
        node: Node being evaluated

    Attribute Value Properties:
        Inputs:
            inputs.a
            inputs.b
            inputs.is_gpu
            inputs.multiplier
            inputs.points
        Outputs:
            outputs.points
            outputs.sum
    """

    # 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:a', 'float', 0, None, 'First value to be added in algorithm 1', {ogn.MetadataKeys.DEFAULT: '0.0'}, True, 0.0, False, ''),
        ('inputs:b', 'float', 0, None, 'Second value to be added in algorithm 1', {ogn.MetadataKeys.DEFAULT: '0.0'}, True, 0.0, False, ''),
        ('inputs:is_gpu', 'bool', 0, None, 'Runtime switch determining where the data for the other attributes lives.', {ogn.MetadataKeys.MEMORY_TYPE: 'cpu', ogn.MetadataKeys.DEFAULT: 'false'}, True, False, False, ''),
        ('inputs:multiplier', 'float3', 0, None, 'Amplitude of the expansion for the input points in algorithm 2', {ogn.MetadataKeys.DEFAULT: '[1.0, 1.0, 1.0]'}, True, [1.0, 1.0, 1.0], False, ''),
        ('inputs:points', 'float3[]', 0, None, 'Points to be moved by algorithm 2', {ogn.MetadataKeys.DEFAULT: '[]'}, True, [], False, ''),
        ('outputs:points', 'float3[]', 0, None, 'Final positions of points from algorithm 2', {}, True, None, False, ''),
        ('outputs:sum', 'float', 0, None, 'Sum of the two inputs from algorithm 1', {}, True, None, False, ''),
    ])

    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._batchedReadAttributes = []
            self._batchedReadValues = []

        class __a:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.a)
                return data_view.get()

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

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.a)
                return data_view.get(on_gpu=True)

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

        @property
        def a(self):
            return self.__class__.__a(self)

        class __b:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.b)
                return data_view.get()

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

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.b)
                return data_view.get(on_gpu=True)

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

        @property
        def b(self):
            return self.__class__.__b(self)

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

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

        class __multiplier:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.multiplier)
                return data_view.get()

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

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.multiplier)
                return data_view.get(on_gpu=True)

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

        @property
        def multiplier(self):
            return self.__class__.__multiplier(self)

        class __points:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.points)
                return data_view.get()

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

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.points)
                return data_view.get(on_gpu=True)

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

        @property
        def points(self):
            return self.__class__.__points(self)

        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.points_size = None
            self._batchedWriteValues = { }

        class __points:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.points)
                return data_view.get(reserved_element_count=self._parent.points_size)

            @cpu.setter
            def cpu(self, value):
                data_view = og.AttributeValueHelper(self._parent._attributes.cpu)
                data_view.set(value)
                self._parent.cpu_size = data_view.get_array_size()

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.points)
                return data_view.get(reserved_element_count=self._parent.points_size, on_gpu=True)

            @gpu.setter
            def gpu(self, value):
                data_view = og.AttributeValueHelper(self._parent._attributes.gpu)
                data_view.set(value)
                self._parent.gpu_size = data_view.get_array_size()

        @property
        def points(self):
            return self.__class__.__points(self)

        class __sum:
            def __init__(self, parent):
                self._parent = parent

            @property
            def cpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.sum)
                return data_view.get()

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

            @property
            def gpu(self):
                data_view = og.AttributeValueHelper(self._parent._attributes.sum)
                return data_view.get(on_gpu=True)

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

        @property
        def sum(self):
            return self.__class__.__sum(self)

        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 = OgnTutorialCpuGpuDataDatabase.ValuesForInputs(node, self.attributes.inputs, dynamic_attributes)
        dynamic_attributes = self.dynamic_attribute_data(node, og.AttributePortType.ATTRIBUTE_PORT_TYPE_OUTPUT)
        self.outputs = OgnTutorialCpuGpuDataDatabase.ValuesForOutputs(node, self.attributes.outputs, dynamic_attributes)
        dynamic_attributes = self.dynamic_attribute_data(node, og.AttributePortType.ATTRIBUTE_PORT_TYPE_STATE)
        self.state = OgnTutorialCpuGpuDataDatabase.ValuesForState(node, self.attributes.state, dynamic_attributes)
