import Vector3 from "../../../../core/geom/Vector3.js";
import { FirstPersonPosture } from "./FirstPersonPosture.js";

/**
 * Read-only outputs intended for a downstream skeleton system to consume.
 *
 * Surfaced as a separate struct (rather than reading {@link FirstPersonState}
 * directly) so that the skeleton-driving contract is stable even if
 * internal state representations change later. A future first-person arms
 * rig, third-person body rig, and network-replicated remote rig should all
 * be able to drive themselves from these channels alone.
 */

/**
 * Vertical (kinetic) state — "what is the body doing in 3D space right now".
 * Independent of {@link FirstPersonLocomotionMode}; the player can be
 * Airborne while sprinting, Anticipating from a crouch, etc.
 * @readonly enum
 */
export const FirstPersonActionState = {
    Grounded: 0,
    Anticipating: 1, // squashing down before jump impulse
    Airborne: 2,
    Landing: 3,
};

/**
 * Horizontal (intent-driven) locomotion mode. Drives the blend tree for the
 * skeletal rig's locomotion layer. Orthogonal to {@link FirstPersonActionState}
 * — a Sprint-mode player who jumps is `(Sprint, Airborne)` until they land.
 * @readonly enum
 */
export const FirstPersonLocomotionMode = {
    Idle: 0,
    Walk: 1,
    Sprint: 2,
    Crouch: 3,
};

export class FirstPersonPose {
    /**
     * World-space body root.
     * @type {Vector3}
     */
    rootPosition = new Vector3();

    /**
     * Body facing yaw (radians, around world up).
     * @type {number}
     */
    rootYawRad = 0;

    /**
     * Head decoupled from body. For most first-person controllers
     * headYawRad === rootYawRad; surfaced separately so future torso-twist
     * rigs can lag the head.
     * @type {number}
     */
    headYawRad = 0;

    /**
     * @type {number}
     */
    headPitchRad = 0;

    /**
     * Includes lean roll.
     * @type {number}
     */
    headRollRad = 0;

    /**
     * Stride phase, [0..1). Same value the camera bob uses, so a feet rig
     * driven by this will land its footfalls on the same frames the camera
     * dips.
     * @type {number}
     */
    locomotionPhase = 0;

    /**
     * m/s, horizontal speed. Drives a blend tree.
     * @type {number}
     */
    locomotionSpeed = 0;

    /**
     * [-1..1] lateral component, for strafe-blend space.
     * @type {number}
     */
    locomotionStrafe = 0;

    /**
     * Kinetic vertical state. See {@link FirstPersonActionState}.
     * @type {number}
     */
    actionState = FirstPersonActionState.Grounded;

    /**
     * Horizontal locomotion intent mode. See {@link FirstPersonLocomotionMode}.
     * @type {number}
     */
    locomotionMode = FirstPersonLocomotionMode.Idle;

    /**
     * [0..1] interpolation between standing and fully-crouched pose.
     * Legacy field — pinned to (height − eyeHeight)/(height − crouchHeight),
     * so a body in Prone reads ≥ 1.0 (saturated). Use {@link postureAmount}
     * if the animator needs to distinguish Crouch / Prone / Hang.
     * @type {number}
     */
    crouchAmount = 0;

    /**
     * Current body shape. Picks which animation track the rig should
     * blend toward — Stand / Crouch / Prone / Hang. See
     * {@link FirstPersonPosture}.
     * @type {number}
     */
    posture = FirstPersonPosture.Stand;

    /**
     * [0..1] blend weight from Stand toward the current {@link posture}.
     * 0 = fully standing (postures hold-frame is the standing rig);
     * 1 = fully in the posture's target shape. Computed from eye-height
     * relative to the posture's target height, so the value transitions
     * smoothly with the same spring the camera uses.
     * @type {number}
     */
    postureAmount = 0;

    /**
     * Same as headPitchRad, surfaced separately for IK consumers that only
     * need the aim axis.
     * @type {number}
     */
    aimPitch = 0;
}
