<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants.js"></script>

<script>
let testName = "Input sources are re-created when handedness or target ray mode changes";

let watcherDone = new Event("watcherdone");

let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;

let testFunction = function(session, fakeDeviceController, t) {
  let eventWatcher = new EventWatcher(t, session, ["watcherdone"]);
  let eventPromise = eventWatcher.wait_for(["watcherdone"]);

  let inputChangeEvents = 0;
  let cached_input_source = null;
  function onInputSourcesChange(event) {
    t.step(() => {
      inputChangeEvents++;
      assert_equals(event.session, session);

      if (inputChangeEvents == 1) {
        // The first change event should be adding our controller.
        validateAdded(event.added, 1);
        validateRemoved(event.removed, 0);
        cached_input_source = getInputSources()[0];
        assert_not_equals(cached_input_source, null);
        assert_equals(cached_input_source.handedness, "none");
        assert_equals(cached_input_source.targetRayMode, "gaze");
        assertProfilesEqual(cached_input_source.profiles, ["a", "b"]);
      } else if (inputChangeEvents == 2) {
        // The second event should be replacing the controller with one that has
        // the updated target ray mode.
        validateInputSourceChange(event, "none", "tracked-pointer", ["a", "b"]);
        cached_input_source = getInputSources()[0];
      } else if (inputChangeEvents == 3) {
        // The third event should be replacing the controller with one that has
        // the updated handedness.
        validateInputSourceChange(event, "left", "tracked-pointer", ["a", "b"]);
        cached_input_source = getInputSources()[0];
      } else if (inputChangeEvents == 4) {
        // The fourth event should be updating the second value in the profiles
        // array.
        validateInputSourceChange(event, "left", "tracked-pointer", ["a", "c"]);
        cached_input_source = getInputSources()[0];
      } else if (inputChangeEvents == 5) {
        // The fifth event should be removing the second value from the profiles
        // array.
        validateInputSourceChange(event, "left", "tracked-pointer", ["a"]);
        session.dispatchEvent(watcherDone);
      }
    });
  }

  function assertProfilesEqual(profiles, expected_profiles) {
    assert_equals(profiles.length, expected_profiles.length);
    for (let i = 0; i < profiles.length; ++i) {
      assert_equals(profiles[i], expected_profiles[i]);
    }
  }

  function validateInputSourceChange(
      event, expected_hand, expected_mode, expected_profiles) {
    validateAdded(event.added, 1);
    validateRemoved(event.removed, 1);
    assert_true(event.removed.includes(cached_input_source));
    assert_false(event.added.includes(cached_input_source));
    let source = event.added[0];
    assert_equals(source.handedness, expected_hand);
    assert_equals(source.targetRayMode, expected_mode);
    assertProfilesEqual(source.profiles, expected_profiles);
  }

  function validateAdded(added, length) {
    t.step(() => {
      assert_not_equals(added, null);
      assert_equals(added.length, length,
          "Added length matches expectations");

      let currentSources = getInputSources();
      added.forEach((source) => {
        assert_true(currentSources.includes(source),
          "Every element in added should be in the input source list");
      });
    });
  }

  function validateRemoved(removed, length) {
    t.step(() => {
      assert_not_equals(removed, null);
        assert_equals(removed.length, length,
            "Removed length matches expectations");

      let currentSources = getInputSources();
      removed.forEach((source) => {
        assert_false(currentSources.includes(source),
          "No element in removed should be in the input source list");
      });
    });
  }

  function getInputSources() {
    return Array.from(session.inputSources.values());
  }

  session.addEventListener('inputsourceschange', onInputSourcesChange, false);

  // Create a gaze based input source with no handedness that we can change
  // to validate SameObject properties.
  let input_source = fakeDeviceController.simulateInputSourceConnection({
    handedness: "none",
    targetRayMode: "gaze",
    pointerOrigin: VALID_POINTER_TRANSFORM,
    profiles: ["a", "b"]
  });

  // Make our first input source change after one frame, and wait an additional
  // frame for that change to propogate. Then make additional changes, waiting
  // one frame after each one to verify that they fired an inputsourceschanged
  // event.
  session.requestAnimationFrame((time, xrFrame) => {
    input_source.setTargetRayMode("tracked-pointer");
    session.requestAnimationFrame((time, xrFrame) => {
      input_source.setHandedness("left");
      session.requestAnimationFrame((time, xrFrame) => {
        input_source.setProfiles(["a", "c"]);
        session.requestAnimationFrame((time, xrFrame) => {
          input_source.setProfiles(["a"]);
          session.requestAnimationFrame((time, xrFrame) => {});
        });
      });
    });
  });

  return eventPromise;
};

xr_session_promise_test(
  testName, testFunction, fakeDeviceInitParams, 'immersive-vr');
</script>
