// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
pub fn register_gamepad_events(
  gamepads : Set[@inputs.Gamepad],
  buttons : Set[@inputs.GamepadButtonInput],
  axes : Map[@inputs.GamepadAxisInput, Double],
) -> Unit {
  canvas_backend.gamepads = Some(gamepads)
  canvas_backend.gamepad_buttons = Some(buttons)
  canvas_backend.gamepad_axes = Some(axes)
  sync_gamepad_events()
}

///|
fn sync_gamepad_events() -> Unit {
  guard canvas_backend.gamepads is Some(gamepads) else { return }
  guard canvas_backend.gamepad_buttons is Some(buttons) else { return }
  guard canvas_backend.gamepad_axes is Some(axes) else { return }

  gamepads.clear()
  buttons.clear()
  axes.clear()

  let count = webgpu_gamepad_count()
  for id in 0.. Unit {
  let mappings : Array[(Int, @inputs.GamepadButton)] = [
    (0, South),
    (1, East),
    (2, West),
    (3, North),
    (4, LeftTrigger),
    (5, RightTrigger),
    (6, LeftTrigger2),
    (7, RightTrigger2),
    (8, Select),
    (9, Start),
    (10, LeftThumb),
    (11, RightThumb),
    (12, DPadUp),
    (13, DPadDown),
    (14, DPadLeft),
    (15, DPadRight),
    (16, Mode),
  ]
  for mapping in mappings {
    if webgpu_gamepad_button_pressed(id, mapping.0) {
      buttons.add(@inputs.GamepadButtonInput::new(gamepad, mapping.1))
    }
  }
}

///|
fn sync_axes(
  id : Int,
  gamepad : @inputs.Gamepad,
  axes : Map[@inputs.GamepadAxisInput, Double],
) -> Unit {
  axes.set(
    @inputs.GamepadAxisInput::new(gamepad, LeftStickX),
    webgpu_gamepad_axis_value(id, 0),
  )
  axes.set(
    @inputs.GamepadAxisInput::new(gamepad, LeftStickY),
    webgpu_gamepad_axis_value(id, 1),
  )
  axes.set(
    @inputs.GamepadAxisInput::new(gamepad, RightStickX),
    webgpu_gamepad_axis_value(id, 2),
  )
  axes.set(
    @inputs.GamepadAxisInput::new(gamepad, RightStickY),
    webgpu_gamepad_axis_value(id, 3),
  )

  // Trigger axes prefer analog button value, fallback to axis slots when provided.
  let left_trigger = max_abs(
    webgpu_gamepad_button_value(id, 6),
    normalize_signed_axis(webgpu_gamepad_axis_value(id, 4)),
  )
  let right_trigger = max_abs(
    webgpu_gamepad_button_value(id, 7),
    normalize_signed_axis(webgpu_gamepad_axis_value(id, 5)),
  )
  axes.set(@inputs.GamepadAxisInput::new(gamepad, LeftZ), left_trigger)
  axes.set(@inputs.GamepadAxisInput::new(gamepad, RightZ), right_trigger)
}

///|
fn normalize_signed_axis(value : Double) -> Double {
  let normalized = (value + 1.0) * 0.5
  if normalized < 0.0 {
    0.0
  } else if normalized > 1.0 {
    1.0
  } else {
    normalized
  }
}

///|
fn max_abs(a : Double, b : Double) -> Double {
  if a.abs() >= b.abs() {
    a
  } else {
    b
  }
}