var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Object3D, Quaternion, Vector3 } from "three";
import { Mathf } from "../engine/engine_math.js";
import { Axes } from "../engine/engine_physics.types.js";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { getWorldPosition, getWorldQuaternion } from "../engine/engine_three_utils.js";
import { Behaviour } from "./Component.js";
/**
 * [SmoothFollow](https://engine.needle.tools/docs/api/SmoothFollow) makes this GameObject smoothly follow another target object's position and/or rotation.
 *
 * **Position Following:**
 * When enabled (`followFactor > 0`), this object will move towards the target's world position.
 * The object interpolates from its current position to the target's position each frame.
 * Use `positionAxes` to restrict following to specific axes (e.g., only horizontal movement).
 *
 * **Rotation Following:**
 * When enabled (`rotateFactor > 0`), this object will rotate to match the target's world rotation.
 * The object smoothly interpolates from its current rotation to the target's rotation each frame.
 * This makes the object face the same direction as the target, not look at it (use {@link LookAt} for that).
 *
 * **Smoothing:**
 * Both position and rotation use time-based interpolation (lerp/slerp).
 * Higher factor values = faster following (less lag), lower values = slower following (more lag).
 * Set a factor to 0 to disable that type of following entirely.
 *
 * **Common Use Cases:**
 * - Camera following a player character
 * - UI elements tracking world objects
 * - Delayed motion effects (ghost trails, spring arms)
 * - Smoothed object attachment
 *
 * @example Follow a target with smooth position
 * ```ts
 * const follower = myObject.addComponent(SmoothFollow);
 * follower.target = playerObject;
 * follower.followFactor = 5;  // Higher = faster following
 * follower.rotateFactor = 0;  // Don't follow rotation
 * ```
 *
 * @example Follow only on horizontal plane
 * ```ts
 * follower.positionAxes = Axes.X | Axes.Z; // Follow X and Z only (no vertical)
 * ```
 *
 * @example Follow both position and rotation
 * ```ts
 * follower.target = targetObject;
 * follower.followFactor = 3;  // Smooth position following
 * follower.rotateFactor = 2;  // Smooth rotation following
 * ```
 *
 * @summary Smoothly follows a target object's position and/or rotation
 * @category Interactivity
 * @group Components
 * @see {@link LookAtConstraint} for making an object look at a target (different from rotation following)
 * @see {@link Mathf} for the interpolation used
 */
export class SmoothFollow extends Behaviour {
    /**
     * The target to follow. If null, the GameObject will not move.
     */
    target = null;
    /**
     * Speed factor for position following.
     * Controls how quickly this object moves to match the target's position.
     * Higher values = faster/tighter following (less lag), lower = slower/looser (more lag).
     * Set to 0 to disable position following entirely.
     * @default 0.1
     */
    followFactor = .1;
    /**
     * Speed factor for rotation following.
     * Controls how quickly this object rotates to match the target's rotation.
     * Higher values = faster/tighter following (less lag), lower = slower/looser (more lag).
     * Set to 0 to disable rotation following entirely.
     * @default 0.1
     */
    rotateFactor = .1;
    /**
     * Which position axes to follow. Use bitwise OR to combine:
     * `Axes.X | Axes.Y` follows only X and Y axes.
     * @default Axes.All
     */
    positionAxes = Axes.All;
    /** When true, rotates 180° around Y axis (useful for mirrored setups) */
    flipForward = false;
    static _invertForward = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI);
    _firstUpdate = true;
    /**
     * Update the position and rotation of the GameObject to follow the target.
     */
    onBeforeRender() {
        this.updateNow(false);
    }
    /**
     * Manually update the position/rotation to follow the target.
     * @param hard If true, snaps instantly to target without smoothing
     */
    updateNow(hard) {
        if (!this.target || this.target === this.gameObject)
            return;
        if (this.followFactor > 0) {
            const wp = getWorldPosition(this.target);
            const fpos = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.followFactor);
            const currentPosition = this.worldPosition;
            if (this.positionAxes & Axes.X)
                currentPosition.x = Mathf.lerp(currentPosition.x, wp.x, fpos);
            if (this.positionAxes & Axes.Y)
                currentPosition.y = Mathf.lerp(currentPosition.y, wp.y, fpos);
            if (this.positionAxes & Axes.Z)
                currentPosition.z = Mathf.lerp(currentPosition.z, wp.z, fpos);
            // TODO lerp distance from target as well
            this.worldPosition = currentPosition;
        }
        if (this.rotateFactor > 0) {
            const wr = getWorldQuaternion(this.target);
            if (this.flipForward) {
                wr.premultiply(SmoothFollow._invertForward);
            }
            const frot = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.rotateFactor);
            this.worldQuaternion = this.worldQuaternion.slerp(wr, frot);
        }
        this._firstUpdate = false;
    }
}
__decorate([
    serializable(Object3D)
], SmoothFollow.prototype, "target", void 0);
__decorate([
    serializable()
], SmoothFollow.prototype, "followFactor", void 0);
__decorate([
    serializable()
], SmoothFollow.prototype, "rotateFactor", void 0);
__decorate([
    serializable()
], SmoothFollow.prototype, "positionAxes", void 0);
//# sourceMappingURL=SmoothFollow.js.map