"""
Implementation of the Python node accessing all of the simple data types.
This class exercises access to the DataModel through the generated database class for all simple data types.
It implements the same algorithm as the C++ node OgnTutorialABI.cpp
"""
import colorsys
from contextlib import suppress

import carb
import omni.graph.core as og


class OgnTutorialABIPy:
    """Illustrate overrides of the various ABI functions available to a Python OmniGraph node"""

    @staticmethod
    def compute(context, node) -> bool:
        """
        Convert a color into components using the raw ABI function without the nice Database interface.

        Rarely Overridden:
            Usually you will implement the much more friendly and Pythonic compute(OgnTutorialABIDatabasePy)
            method so that you can have easier access to your data.
        """
        # Manually acquire the data on the known attributes
        input_color_attr = node.get_attribute("inputs:color")

        # Extract the input value from the controller
        input_color = og.Controller.get(input_color_attr)

        output_hue_attr = node.get_attribute("outputs:h")
        output_saturation_attr = node.get_attribute("outputs:s")
        output_value_attr = node.get_attribute("outputs:v")

        (h, s, v) = colorsys.rgb_to_hsv(*input_color)

        # This exception is triggered if you accidentally reverse the parameters to set_attr_value.
        # The error isn't recovered, to prevent proliferation of inconsistent calls. The exception is
        # thrown to help with debugging. (As this is an example the exception is caught and ignored here.)
        with suppress(og.OmniGraphError):
            og.Controller.set(h, output_hue_attr)

        og.Controller.set(output_hue_attr, h)
        og.Controller.set(output_saturation_attr, s)
        og.Controller.set(output_value_attr, v)

        #
        # For comparison, here is the same algorithm implemented using "compute(db)"
        #
        # def compute(db) -> bool:
        #     (db.outputs.h, db.outputs.s, db.outputs.v) = colorsys.rgb_to_hsv(*db.inputs.color)
        return True

    # ----------------------------------------------------------------------
    @staticmethod
    def get_node_type() -> str:
        """
        Rarely overridden

        This should almost never be overridden as the auto-generated code will handle the name
        """
        carb.log_info("Python ABI override of get_node_type")
        return "omni.graph.tutorials.AbiPy"

    # ----------------------------------------------------------------------
    @staticmethod
    def initialize(graph_context, node):
        """
        Occasionally overridden

        This method might be overridden to set up initial conditions when a node of this type is created.
        Note that overridding this puts the onus on the node writer to set up initial conditions such as
        attribute default values and metadata.

        When a node is created this will be called
        """
        carb.log_info("Python ABI override of initialize")
        # There is no default behaviour on initialize so nothing else is needed for this tutorial to function

    # ----------------------------------------------------------------------
    @staticmethod
    def initialize_type(node_type) -> bool:
        """
        Rarely overridden

        This method might be overridden to set up initial conditions when a node type is registered.
        Note that overriding this puts the onus on the node writer to initialize the attributes and metadata.

        By returning "True" the function is requesting that the attributes and metadata be initialized upon return,
        otherwise the caller will assume that this override has already done that.
        """
        carb.log_info("Python ABI override of initialize_type")
        return True

    # ----------------------------------------------------------------------
    @staticmethod
    def release(node):
        """
        Occasionally overridden

        After a node is removed it will get a release call where anything set up in initialize() can be torn down
        """
        carb.log_info("Python ABI override of release")
        # There is no default behaviour on release so nothing else is needed for this tutorial to function

    # ----------------------------------------------------------------------
    @staticmethod
    def update_node_version(graph_context, node, old_version: int, new_version: int):
        """
        Occasionally overridden

        This is something you do want to override when you have more than version of your node.
        In it you would translate attribute information from older versions into the current one.
        """
        carb.log_info(f"Python ABI override of update_node_version from {old_version} to {new_version}")
        # There is no default behaviour on update_node_version so nothing else is needed for this tutorial to function
        return old_version < new_version

    # ----------------------------------------------------------------------
    @staticmethod
    def on_connection_type_resolve(node):
        """
        Occasionally overridden

        When there is a connection change to this node which results in an extended type attribute being automatically
        resolved, this callback gives the node a change to resolve other extended type attributes. For example a generic
        'Increment' node can resolve its output to an int only after its input has been resolved to an int. Attribute
        types are resolved using omni.graph.attribute.set_resolved_type(), or the utility functions such as
        og.resolve_fully_coupled().
        """
        carb.log_info("Python ABI override of on_connection_type_resolve")
        # There is no default behaviour for on_connection_type_resolve so nothing else is needed for this
        # tutorial to function
