/** Device adapters for the renderer-independent vehicle action contract. */

import {
  normalizeVehicleActions,
  type VehicleActionSnapshot,
} from "../transport/vehicleController.ts";

export interface GamepadButtonLike {
  pressed: boolean;
  value: number;
}

export interface GamepadLike {
  axes: readonly number[];
  buttons: readonly GamepadButtonLike[];
}

export interface GamepadButtonState {
  assist: boolean;
  reset: boolean;
}

export interface GamepadVehicleSample {
  actions: VehicleActionSnapshot;
  buttons: GamepadButtonState;
}

function axis(value: number | undefined, deadzone = 0.12): number {
  if (!Number.isFinite(value)) return 0;
  const clamped = Math.max(-1, Math.min(1, value ?? 0));
  if (Math.abs(clamped) <= deadzone) return 0;
  return Math.sign(clamped) * ((Math.abs(clamped) - deadzone) / (1 - deadzone));
}

function button(pad: GamepadLike, index: number): number {
  const found = pad.buttons[index];
  if (!found) return 0;
  return Math.max(0, Math.min(1, Number.isFinite(found.value) ? found.value : found.pressed ? 1 : 0));
}

/**
 * Standard-layout mapping: left stick steers, triggers brake/throttle, B is
 * handbrake, Y resumes assistance, and X resets. Mode/reset are rising edges.
 */
export function sampleStandardGamepad(
  pad: GamepadLike,
  previous: GamepadButtonState = { assist: false, reset: false },
): GamepadVehicleSample {
  const buttons = {
    assist: button(pad, 3) > 0.5,
    reset: button(pad, 2) > 0.5,
  };
  return {
    actions: normalizeVehicleActions({
      steering: axis(pad.axes[0]),
      brake: button(pad, 6),
      throttle: button(pad, 7),
      handbrake: button(pad, 1) > 0.5,
      modeRequest: buttons.assist && !previous.assist ? "assisted" : "none",
      reset: buttons.reset && !previous.reset,
    }),
    buttons,
  };
}

/** Keyboard/touch and gamepad may be used together; strongest intent wins. */
export function mergeVehicleActions(
  primary: Partial<VehicleActionSnapshot>,
  secondary: Partial<VehicleActionSnapshot>,
): VehicleActionSnapshot {
  const a = normalizeVehicleActions(primary);
  const b = normalizeVehicleActions(secondary);
  return normalizeVehicleActions({
    throttle: Math.max(a.throttle, b.throttle),
    brake: Math.max(a.brake, b.brake),
    steering: Math.abs(b.steering) > Math.abs(a.steering) ? b.steering : a.steering,
    handbrake: a.handbrake || b.handbrake,
    modeRequest: b.modeRequest !== "none" ? b.modeRequest : a.modeRequest,
    reset: a.reset || b.reset,
  });
}
