<!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 = "WebXR InputSource's gamepad properly registers input";

let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;

let testFunction = function(session, fakeDeviceController, t) {

  // There should only be one input source change event, which is from adding
  // the input source at the start of the test.
  let inputChangeEvents = 0;
  function onInputSourcesChange(event) {
    assert_equals(inputChangeEvents, 0,
      "Gamepad button or input axis value changes should not fire an input source change event.");
    inputChangeEvents++;
  }

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

  // Create our input source and immediately toggle the primary input so that
  // it appears as already needing to send a click event when it appears.
  let input_source = fakeDeviceController.simulateInputSourceConnection({
    handedness: "right",
    targetRayMode: "tracked-pointer",
    pointerOrigin: VALID_POINTER_TRANSFORM,
    profiles: [],
    supportedButtons: [
      {
        buttonType: 'grip',
        pressed: false,
        touched: false,
        pressedValue: 0
      },
      {
        buttonType: 'touchpad',
        pressed: false,
        touched: false,
        pressedValue: 0
      }
    ]
  });

  let cached_input_source = null;
  let cached_gamepad = null;

  function assertSameObjects() {
    assert_equals(session.inputSources[0], cached_input_source);
    assert_equals(cached_input_source.gamepad, cached_gamepad);

    // Also make sure that WebXR gamepads have the index and id values required
    // by the spec.
    assert_equals(cached_gamepad.index, -1);
    assert_equals(cached_gamepad.id, "");
  }

  // Input events and gamepad state changes (button presses, axis movements)
  // need one frame to propagate, so this does (in order and running a rAF after
  // each step):
  // 1) Press the mock gamepad's button (so we can verify the button press makes
  //    its way to the WebXR gamepad and that it does not fire an
  //    inputsourceschange event).
  // 2) Update the mock gamepad's input axes values (so we can verify the
  //    updated values make their way to the WebXR gamepad and that it does not
  //    fire an inputsourceschange event).
  return new Promise((resolve) => {
    requestSkipAnimationFrame(session, () => {
      // Make sure the exposed gamepad has the number of buttons and axes we
      // requested.
      // 3 Buttons: trigger, grip, touchpad
      // 2 Axes from the touchpad
      cached_input_source = session.inputSources[0];
      cached_gamepad = cached_input_source.gamepad;
      t.step(() => {
        assert_equals(cached_gamepad.index, -1);
        assert_equals(cached_gamepad.id, "");
        assert_equals(cached_gamepad.buttons.length, 3);
        assert_equals(cached_gamepad.axes.length, 2);
        // Initially, the button should not be pressed and the axes values should
        // be set to 0.
        assert_false(cached_gamepad.buttons[1].pressed);
        assert_equals(cached_gamepad.axes[0], 0);
        assert_equals(cached_gamepad.axes[1], 0);
      }, "Verify initial state");
      // Simulate button press.
      input_source.updateButtonState({
        buttonType: 'grip',
        pressed: true,
        touched: true,
        value: 1.0
      });
      session.requestAnimationFrame(() => {
        t.step(() => {
          assertSameObjects();
          assert_true(cached_gamepad.buttons[1].pressed);
        }, "Gamepad is updated in place when a button is pressed");

        // Simulate input axes movement.
        input_source.updateButtonState({
          buttonType: 'touchpad',
          pressed: false,
          touched: true,
          value: 0,
          xValue: 0.5,
          yValue: -0.5
        });
        session.requestAnimationFrame(() => {
          // Input source and gamepad should not be re-created. They should be
          // updated in place when input axes values change.
          t.step(() => {
            assertSameObjects();
            assert_equals(cached_gamepad.axes[0], 0.5);
            assert_equals(cached_gamepad.axes[1], -0.5);
            // Button that was pressed last frame should still be pressed.
            assert_true(cached_gamepad.buttons[1].pressed);
          }, "Gamepad is updated in place when axes values change");
          resolve();
        });
      });
    });
  });
};

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