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

Computes the floating point remainder of A / B. If B is zero, the result is zero. The returned value has the same sign as
A
"""

from typing import Any
import sys
import traceback

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



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

    Class Members:
        node: Node being evaluated

    Attribute Value Properties:
        Inputs:
            inputs.a
            inputs.b
        Outputs:
            outputs.result
    """

    # 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', 'colord[3],colord[3][],colord[4],colord[4][],colorf[3],colorf[3][],colorf[4],colorf[4][],colorh[3],colorh[3][],colorh[4],colorh[4][],double,double[2],double[2][],double[3],double[3][],double[4],double[4][],double[],float,float[2],float[2][],float[3],float[3][],float[4],float[4][],float[],half,half[2],half[2][],half[3],half[3][],half[4],half[4][],half[],normald[3],normald[3][],normalf[3],normalf[3][],normalh[3],normalh[3][],pointd[3],pointd[3][],pointf[3],pointf[3][],pointh[3],pointh[3][],quatd[4],quatd[4][],quatf[4],quatf[4][],quath[4],quath[4][],texcoordd[2],texcoordd[2][],texcoordd[3],texcoordd[3][],texcoordf[2],texcoordf[2][],texcoordf[3],texcoordf[3][],texcoordh[2],texcoordh[2][],texcoordh[3],texcoordh[3][],timecode,timecode[],vectord[3],vectord[3][],vectorf[3],vectorf[3][],vectorh[3],vectorh[3][]', 1, 'A', 'The dividend of (A / B)', {}, True, None, False, ''),
        ('inputs:b', 'colord[3],colord[3][],colord[4],colord[4][],colorf[3],colorf[3][],colorf[4],colorf[4][],colorh[3],colorh[3][],colorh[4],colorh[4][],double,double[2],double[2][],double[3],double[3][],double[4],double[4][],double[],float,float[2],float[2][],float[3],float[3][],float[4],float[4][],float[],half,half[2],half[2][],half[3],half[3][],half[4],half[4][],half[],normald[3],normald[3][],normalf[3],normalf[3][],normalh[3],normalh[3][],pointd[3],pointd[3][],pointf[3],pointf[3][],pointh[3],pointh[3][],quatd[4],quatd[4][],quatf[4],quatf[4][],quath[4],quath[4][],texcoordd[2],texcoordd[2][],texcoordd[3],texcoordd[3][],texcoordf[2],texcoordf[2][],texcoordf[3],texcoordf[3][],texcoordh[2],texcoordh[2][],texcoordh[3],texcoordh[3][],timecode,timecode[],vectord[3],vectord[3][],vectorf[3],vectorf[3][],vectorh[3],vectorh[3][]', 1, 'B', 'The divisor of (A / B)', {}, True, None, False, ''),
        ('outputs:result', 'colord[3],colord[3][],colord[4],colord[4][],colorf[3],colorf[3][],colorf[4],colorf[4][],colorh[3],colorh[3][],colorh[4],colorh[4][],double,double[2],double[2][],double[3],double[3][],double[4],double[4][],double[],float,float[2],float[2][],float[3],float[3][],float[4],float[4][],float[],half,half[2],half[2][],half[3],half[3][],half[4],half[4][],half[],normald[3],normald[3][],normalf[3],normalf[3][],normalh[3],normalh[3][],pointd[3],pointd[3][],pointf[3],pointf[3][],pointh[3],pointh[3][],quatd[4],quatd[4][],quatf[4],quatf[4][],quath[4],quath[4][],texcoordd[2],texcoordd[2][],texcoordd[3],texcoordd[3][],texcoordf[2],texcoordf[2][],texcoordf[3],texcoordf[3][],texcoordh[2],texcoordh[2][],texcoordh[3],texcoordh[3][],timecode,timecode[],vectord[3],vectord[3][],vectorf[3],vectorf[3][],vectorh[3],vectorh[3][]', 1, 'Result', 'The floating point remainder of A / B', {}, 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 = []

        @property
        def a(self) -> og.RuntimeAttribute:
            """Get the runtime wrapper class for the attribute inputs.a"""
            return og.RuntimeAttribute(self._attributes.a.get_attribute_data(), self._context, True)

        @a.setter
        def a(self, value_to_set: Any):
            """Assign another attribute's value to outputs.a"""
            if isinstance(value_to_set, og.RuntimeAttribute):
                self.a.value = value_to_set.value
            else:
                self.a.value = value_to_set

        @property
        def b(self) -> og.RuntimeAttribute:
            """Get the runtime wrapper class for the attribute inputs.b"""
            return og.RuntimeAttribute(self._attributes.b.get_attribute_data(), self._context, True)

        @b.setter
        def b(self, value_to_set: Any):
            """Assign another attribute's value to outputs.b"""
            if isinstance(value_to_set, og.RuntimeAttribute):
                self.b.value = value_to_set.value
            else:
                self.b.value = value_to_set

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

        @property
        def result(self) -> og.RuntimeAttribute:
            """Get the runtime wrapper class for the attribute outputs.result"""
            return og.RuntimeAttribute(self._attributes.result.get_attribute_data(), self._context, False)

        @result.setter
        def result(self, value_to_set: Any):
            """Assign another attribute's value to outputs.result"""
            if isinstance(value_to_set, og.RuntimeAttribute):
                self.result.value = value_to_set.value
            else:
                self.result.value = value_to_set

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

    class abi:
        """Class defining the ABI interface for the node type"""

        @staticmethod
        def get_node_type():
            get_node_type_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'get_node_type', None)
            if callable(get_node_type_function):
                return get_node_type_function()
            return 'omni.graph.nodes.FMod'

        @staticmethod
        def compute(context, node):
            def database_valid():
                if db.inputs.a.type.base_type == og.BaseDataType.UNKNOWN:
                    db.log_warning('Required extended attribute inputs:a is not resolved, compute skipped')
                    return False
                if db.inputs.b.type.base_type == og.BaseDataType.UNKNOWN:
                    db.log_warning('Required extended attribute inputs:b is not resolved, compute skipped')
                    return False
                if db.outputs.result.type.base_type == og.BaseDataType.UNKNOWN:
                    db.log_warning('Required extended attribute outputs:result is not resolved, compute skipped')
                    return False
                return True
            try:
                per_node_data = OgnFModDatabase.PER_NODE_DATA[node.node_id()]
                db = per_node_data.get('_db')
                if db is None:
                    db = OgnFModDatabase(node)
                    per_node_data['_db'] = db
                if not database_valid():
                    per_node_data['_db'] = None
                    return False
            except:
                db = OgnFModDatabase(node)

            try:
                compute_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'compute', None)
                if callable(compute_function) and compute_function.__code__.co_argcount > 1:
                    return compute_function(context, node)

                db.inputs._prefetch()
                db.inputs._setting_locked = True
                with og.in_compute():
                    return OgnFModDatabase.NODE_TYPE_CLASS.compute(db)
            except Exception as error:
                stack_trace = "".join(traceback.format_tb(sys.exc_info()[2].tb_next))
                db.log_error(f'Assertion raised in compute - {error}\n{stack_trace}', add_context=False)
            finally:
                db.inputs._setting_locked = False
                db.outputs._commit()
            return False

        @staticmethod
        def initialize(context, node):
            OgnFModDatabase._initialize_per_node_data(node)
            initialize_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'initialize', None)
            if callable(initialize_function):
                initialize_function(context, node)

            per_node_data = OgnFModDatabase.PER_NODE_DATA[node.node_id()]
            def on_connection_or_disconnection(*args):
                per_node_data['_db'] = None

            node.register_on_connected_callback(on_connection_or_disconnection)
            node.register_on_disconnected_callback(on_connection_or_disconnection)

        @staticmethod
        def release(node):
            release_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'release', None)
            if callable(release_function):
                release_function(node)
            OgnFModDatabase._release_per_node_data(node)

        @staticmethod
        def release_instance(node, target):
            OgnFModDatabase._release_per_node_instance_data(node, target)

        @staticmethod
        def update_node_version(context, node, old_version, new_version):
            update_node_version_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'update_node_version', None)
            if callable(update_node_version_function):
                return update_node_version_function(context, node, old_version, new_version)
            return False

        @staticmethod
        def initialize_type(node_type):
            initialize_type_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'initialize_type', None)
            needs_initializing = True
            if callable(initialize_type_function):
                needs_initializing = initialize_type_function(node_type)
            if needs_initializing:
                node_type.set_metadata(ogn.MetadataKeys.EXTENSION, "omni.graph.nodes")
                node_type.set_metadata(ogn.MetadataKeys.UI_NAME, "Float Remainder")
                node_type.set_metadata(ogn.MetadataKeys.CATEGORIES, "math:operator")
                node_type.set_metadata(ogn.MetadataKeys.DESCRIPTION, "Computes the floating point remainder of A / B. If B is zero, the result is zero. The returned value has the same sign as A")
                node_type.set_metadata(ogn.MetadataKeys.LANGUAGE, "Python")
                OgnFModDatabase.INTERFACE.add_to_node_type(node_type)

        @staticmethod
        def on_connection_type_resolve(node):
            on_connection_type_resolve_function = getattr(OgnFModDatabase.NODE_TYPE_CLASS, 'on_connection_type_resolve', None)
            if callable(on_connection_type_resolve_function):
                on_connection_type_resolve_function(node)

    NODE_TYPE_CLASS = None

    @staticmethod
    def register(node_type_class):
        OgnFModDatabase.NODE_TYPE_CLASS = node_type_class
        og.register_node_type(OgnFModDatabase.abi, 1)

    @staticmethod
    def deregister():
        og.deregister_node_type("omni.graph.nodes.FMod")
