"""
Tests that verify action UI-dependent nodes
"""
import unittest

import omni.graph.core as og
import omni.graph.core.tests as ogts
import omni.kit.test
import omni.usd
from omni.kit.test.teamcity import is_running_in_teamcity
from omni.kit.viewport.utility import get_active_viewport
from omni.kit.viewport.utility.camera_state import ViewportCameraState
from pxr import Gf


# ======================================================================
class TestOmniGraphAction(ogts.OmniGraphTestCase):
    """Encapsulate simple sanity tests"""

    # ----------------------------------------------------------------------
    @unittest.skipIf(is_running_in_teamcity(), "Crashes on TC / Linux")
    async def test_camera_nodes(self):
        """Test camera-related node basic functionality"""
        keys = og.Controller.Keys
        controller = og.Controller()

        viewport_api = get_active_viewport()
        active_cam = viewport_api.camera_path
        persp_cam = "/OmniverseKit_Persp"
        self.assertEqual(active_cam.pathString, persp_cam)

        (graph, _, _, _) = controller.edit(
            "/TestGraph",
            {
                keys.CREATE_NODES: [
                    ("OnTick", "omni.graph.action.OnTick"),
                    ("SetCam", "omni.graph.ui.SetActiveViewportCamera"),
                ],
                keys.SET_VALUES: [
                    ("SetCam.inputs:primPath", "/OmniverseKit_Top"),
                ],
                keys.CONNECT: [("OnTick.outputs:tick", "SetCam.inputs:execIn")],
            },
        )
        await controller.evaluate()
        active_cam = viewport_api.camera_path
        self.assertEqual(active_cam.pathString, "/OmniverseKit_Top")
        # be nice - set it back
        viewport_api.camera_path = persp_cam

        # Tests moving the camera and camera target
        controller.edit(
            graph,
            {
                keys.DISCONNECT: [("OnTick.outputs:tick", "SetCam.inputs:execIn")],
            },
        )
        await controller.evaluate()
        (graph, (_, _, get_pos, get_target), _, _) = controller.edit(
            graph,
            {
                keys.CREATE_NODES: [
                    ("SetPos", "omni.graph.ui.SetCameraPosition"),
                    ("SetTarget", "omni.graph.ui.SetCameraTarget"),
                    ("GetPos", "omni.graph.ui.GetCameraPosition"),
                    ("GetTarget", "omni.graph.ui.GetCameraTarget"),
                ],
                keys.SET_VALUES: [
                    ("SetPos.inputs:primPath", persp_cam),
                    ("SetPos.inputs:position", Gf.Vec3d(1.0, 2.0, 3.0)),
                    # Target should be different than position
                    ("SetTarget.inputs:target", Gf.Vec3d(-1.0, -2.0, -3.0)),
                    ("SetTarget.inputs:primPath", persp_cam),
                    ("GetPos.inputs:primPath", persp_cam),
                    ("GetTarget.inputs:primPath", persp_cam),
                ],
                keys.CONNECT: [
                    ("OnTick.outputs:tick", "SetPos.inputs:execIn"),
                    ("SetPos.outputs:execOut", "SetTarget.inputs:execIn"),
                ],
            },
        )
        await controller.evaluate()
        # XXX: May need to force these calls through legacy_viewport as C++ nodes call through to that interface.
        camera_state = ViewportCameraState(persp_cam)  # , viewport=viewport_api, force_legacy_api=True)
        x, y, z = camera_state.position_world
        for a, b in zip([x, y, z], [1.0, 2.0, 3.0]):
            self.assertAlmostEqual(a, b, places=5)
        x, y, z = camera_state.target_world
        for a, b in zip([x, y, z], [-1.0, -2.0, -3.0]):
            self.assertAlmostEqual(a, b, places=5)
        # evaluate again, because push-evaluator may not have scheduled the getters after the setters
        await controller.evaluate()
        get_pos = og.Controller.get(controller.attribute(("outputs:position", get_pos)))
        for a, b in zip(get_pos, [1.0, 2.0, 3.0]):
            self.assertAlmostEqual(a, b, places=5)
        get_target = og.Controller.get(controller.attribute(("outputs:target", get_target)))
        for a, b in zip(get_target, [-1.0, -2.0, -3.0]):
            self.assertAlmostEqual(a, b, places=5)

    # ----------------------------------------------------------------------
    @unittest.skipIf(is_running_in_teamcity(), "Flaky - needs investigation")
    async def test_on_new_frame(self):
        """Test OnNewFrame node"""
        keys = og.Controller.Keys
        controller = og.Controller()
        # Check that the NewFrame node is getting updated when frames are being generated
        (_, (new_frame_node,), _, _) = controller.edit(
            "/TestGraph", {keys.CREATE_NODES: [("NewFrame", "omni.graph.ui.OnNewFrame")]}
        )
        attr = controller.attribute(("outputs:frameNumber", new_frame_node))
        await og.Controller.evaluate()
        frame_num = og.Controller.get(attr)
        # Need to tick Kit before evaluation in order to update
        for _ in range(5):
            await omni.kit.app.get_app().next_update_async()
        await og.Controller.evaluate()
        self.assertLess(frame_num, og.Controller.get(attr))
