"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.

SPDX-License-Identifier: Apache-2.0 OR MIT
"""

# Test case ID : C12868580
# Test Case Title : Check that spline follow force works if transform components of entity are altered



# fmt: off
class Tests():
    enter_game_mode             = ("Entered game mode",                   "Failed to enter game mode")
    find_sphere                 = ("Sphere entity found",                 "Sphere entity not found")
    find_force_region           = ("Force region found",                  "Force region not found")
    find_trigger_0              = ("Trigger0 entity found",               "Trigger0 entity not found")
    find_trigger_1              = ("Trigger1 entity found",               "Trigger1 entity not found")
    find_trigger_2              = ("Trigger2 entity found",               "Trigger2 entity not found")
    find_trigger_3              = ("Trigger3 entity found",               "Trigger3 entity not found")
    triggers_positioned_apart   = ("All triggers were positioned apart",  "All triggers were not positioned apart")
    sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
    sphere_reached_trigger0     = ("The sphere reached Trigger0",         "The sphere did not reach Trigger0 before timeout")
    sphere_reached_trigger1     = ("The sphere reached Trigger1",         "The sphere did not reach Trigger1 before timeout")
    sphere_reached_trigger2     = ("The sphere reached Trigger2",         "The sphere did not reach Trigger2 before timeout")
    sphere_reached_trigger3     = ("The sphere reached Trigger3",         "The sphere did not reach Trigger3 before timeout")
    exit_game_mode              = ("Exited game mode",                    "Couldn't exit game mode")
# fmt: on


def C12868580_ForceRegion_SplineModifiedTransform():
    """
    Summary:
    Runs an automated test to ensure that a PhysX force region correctly exerts spline follow force on rigid bodies when
    its transform component has been modified

    Level Description:
    A sphere entity is positioned outside (in the +x direction) a force region entity.
    The sphere has a PhysX collider (sphere) and rigidbody component with gravity disabled, and an initial velocity of
    -3 m/s (in the x direction)

    The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
    It also has a bezier spline component with 4 nodes. Each node is connected in a meandering path through the region.

             [3]~~~[2]
                    )
        O -> [0]~~~[1]
    (sphere)

    The force region is transformed 45 degrees around the Z axis, and scaled by 2 units in the X, Y, and Z directions.

    There are also 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.

    Expected Behavior:
    The sphere will enter into the force region and begin to follow the spline. It will visit each node in order.

    Test Steps:
    1) Open level
    2) Enter game mode
    3) Find entities
    4) Verify triggers are apart
    5) Wait for sphere to complete path
    6) Exit game mode
    7) Close the editor

    Note:
    - This test file must be called from the Open 3D Engine Editor command terminal
    - Any passed and failed tests are written to the Editor.log file.
            Parsing the file or running a log_monitor are required to observe the test results.

    :return: None
    """
    import os
    import sys

    import ImportPathHelper as imports

    imports.init()


    from editor_python_test_tools.utils import Report
    from editor_python_test_tools.utils import TestHelper as helper

    import itertools
    import azlmbr
    import azlmbr.legacy.general as general
    import azlmbr.bus as bus

    # region Constants
    TIMEOUT = 5.0
    MIN_TRIGGER_DISTANCE = 2.0
    # endregion

    # region Entity Classes
    class Sphere:
        def __init__(self, name):
            self.name = name
            self.id = general.find_game_entity(name)

        def get_velocity(self):
            return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)

    class Trigger:
        def __init__(self, name, valid_test, triggered_test):
            self.name = name
            self.id = general.find_game_entity(name)
            self.valid_test = valid_test
            self.triggered_test = triggered_test
            self.triggered = False
            self.create_handler()

        def get_position(self):
            return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)

        def on_trigger_enter(self, args):
            self.triggered = True

        def create_handler(self):
            self.handler = azlmbr.physics.TriggerNotificationBusHandler()
            self.handler.connect(self.id)
            self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)

    # endregion

    # region Helper Functions
    def are_apart(position1, position2, distance):
        return position1.GetDistance(position2) >= distance

    # endregion

    helper.init_idle()
    # 1) Open level
    helper.open_level("Physics", "C12868580_ForceRegion_SplineModifiedTransform")

    # 2) Enter game mode
    helper.enter_game_mode(Tests.enter_game_mode)

    # 3) Find Entities
    sphere = Sphere("Sphere")
    Report.critical_result(Tests.find_sphere, sphere.id.IsValid())

    force_region = Trigger("ForceRegion", Tests.find_force_region, Tests.sphere_entered_force_region)
    trigger0 = Trigger("Trigger0", Tests.find_trigger_0, Tests.sphere_reached_trigger0)
    trigger1 = Trigger("Trigger1", Tests.find_trigger_1, Tests.sphere_reached_trigger1)
    trigger2 = Trigger("Trigger2", Tests.find_trigger_2, Tests.sphere_reached_trigger2)
    trigger3 = Trigger("Trigger3", Tests.find_trigger_3, Tests.sphere_reached_trigger3)
    all_triggers = (force_region, trigger0, trigger1, trigger2, trigger3)

    for trigger in all_triggers:
        Report.critical_result(trigger.valid_test, trigger.id.IsValid())

    # 4) Verify triggers are apart
    all_triggers_apart = True
    for trigger_a, trigger_b in itertools.combinations(all_triggers, 2):
        if not are_apart(trigger_a.get_position(), trigger_b.get_position(), MIN_TRIGGER_DISTANCE):
            Report.info("{} was not far enough away from {}".format(trigger_a.name, trigger_b.name))
            all_triggers_apart = False

    Report.critical_result(Tests.triggers_positioned_apart, all_triggers_apart)

    # 5) Wait for sphere to complete path
    for trigger in all_triggers:
        Report.result(trigger.triggered_test, helper.wait_for_condition(lambda: trigger.triggered, TIMEOUT))

    # 6) Exit game mode
    helper.exit_game_mode(Tests.exit_game_mode)


if __name__ == "__main__":
    import ImportPathHelper as imports
    imports.init()

    from editor_python_test_tools.utils import Report
    Report.start_test(C12868580_ForceRegion_SplineModifiedTransform)
