///|
pub(all) struct InputSnapshot {
  pressed_keys : Set[Code]
  mouse : Mouse
  mut mouse_relative_delta : @math.Vec2?
  mouse_wheel : MouseWheel
  touches : Map[TouchId, TouchPoint]
  touch_changes : Array[TouchEvent]
  connected_gamepads : Set[Gamepad]
  pressed_gamepad_buttons : Set[GamepadButtonInput]
  gamepad_axis_values : Map[GamepadAxisInput, Double]
}

///|
pub fn empty_input_snapshot() -> InputSnapshot {
  {
    pressed_keys: Set([]),
    mouse: {
      pos: @math.Vec2::zero(),
      left_button: false,
      right_button: false,
      middle_button: false,
    },
    mouse_relative_delta: None,
    mouse_wheel: { delta: @math.Vec2::zero() },
    touches: Map([]),
    touch_changes: [],
    connected_gamepads: Set([]),
    pressed_gamepad_buttons: Set([]),
    gamepad_axis_values: Map([]),
  }
}

///|
pub fn apply_input_snapshot(snapshot : InputSnapshot) -> Unit {
  pressed_keys.clear()
  for key in snapshot.pressed_keys {
    pressed_keys.add(key)
  }

  mouse.pos = snapshot.mouse.pos
  mouse.left_button = snapshot.mouse.left_button
  mouse.right_button = snapshot.mouse.right_button
  mouse.middle_button = snapshot.mouse.middle_button

  pending_mouse_relative_delta.val = snapshot.mouse_relative_delta
  mouse_wheel.delta = snapshot.mouse_wheel.delta
  replace_touch_state(snapshot.touches, snapshot.touch_changes)
  apply_primary_touch_mouse_compat(snapshot)

  connected_gamepads().clear()
  for gamepad in snapshot.connected_gamepads {
    connected_gamepads().add(gamepad)
  }

  pressed_gamepad_buttons().clear()
  for button in snapshot.pressed_gamepad_buttons {
    pressed_gamepad_buttons().add(button)
  }

  gamepad_axis_values().clear()
  for axis, value in snapshot.gamepad_axis_values {
    gamepad_axis_values().set(axis, value)
  }
}

///|
fn apply_primary_touch_mouse_compat(snapshot : InputSnapshot) -> Unit {
  let mut mirrored = false
  for _, touch in snapshot.touches {
    if touch.primary {
      mouse.pos = touch.position
      mouse.left_button = true
      mirrored = true
    }
  }
  if mirrored {
    return
  }
  for event in snapshot.touch_changes {
    if event.primary {
      mouse.pos = event.position
      mouse.left_button = match event.phase {
        Ended | Cancelled => false
        Started | Moved => true
      }
    }
  }
}