export interface CameraOrbitState {
  yaw: number;
  pitch: number;
}

export interface PlanarMovement {
  x: number;
  z: number;
}

export const DEFAULT_CAMERA_YAW = 0;
export const DEFAULT_CAMERA_PITCH = Math.atan2(4.45, 7.7);
export const CAMERA_ORBIT_DISTANCE = Math.hypot(4.45, 7.7);
export const CAMERA_FOCUS_HEIGHT = 0.65;
export const CAMERA_LOOK_AHEAD = 2.8;
export const CAMERA_YAW_SPEED = 2.25;
export const CAMERA_PITCH_SPEED = 1.25;
export const MIN_CAMERA_PITCH = 0.24;
export const MAX_CAMERA_PITCH = 0.9;

/** Updates one reusable orbit state without allocating in the render loop. */
export function updateCameraOrbit(
  orbit: CameraOrbitState,
  lookX: number,
  lookY: number,
  deltaSeconds: number,
) {
  orbit.yaw -= lookX * CAMERA_YAW_SPEED * deltaSeconds;
  orbit.pitch = Math.min(
    MAX_CAMERA_PITCH,
    Math.max(MIN_CAMERA_PITCH, orbit.pitch + lookY * CAMERA_PITCH_SPEED * deltaSeconds),
  );
  if (orbit.yaw > Math.PI || orbit.yaw < -Math.PI) {
    orbit.yaw = Math.atan2(Math.sin(orbit.yaw), Math.cos(orbit.yaw));
  }
}

/** Converts left-stick input into world movement relative to current camera yaw. */
export function setCameraRelativeMovement(
  output: PlanarMovement,
  moveX: number,
  moveY: number,
  cameraYaw: number,
) {
  const sinYaw = Math.sin(cameraYaw);
  const cosYaw = Math.cos(cameraYaw);
  output.x = moveX * cosYaw + moveY * sinYaw;
  output.z = -moveX * sinYaw + moveY * cosYaw;
}
