import Vector3 from "../../../../core/geom/Vector3.js";

/**
 * Single spatial-query result. Set fields when `hit` is true; ignore them
 * otherwise. `surfaceTag` is optional — populated by future surface-tag
 * systems (grass/wood/metal/etc.) so abilities and audio can react to the
 * material.
 */
export class SensorHit {
    constructor() {
        this.hit = false;
        /** Distance from query origin to contact point (meters). */
        this.distance = 0;
        /** World-space contact point. */
        this.point = new Vector3();
        /** Surface normal at contact (unit vector, points away from surface). */
        this.normal = new Vector3(0, 1, 0);
        /** Optional material/surface tag — e.g. "grass", "metal". */
        this.surfaceTag = null;
    }

    /** Reset to "no hit" — call before re-using the slot each tick. */
    clear() {
        this.hit = false;
        this.distance = 0;
        this.point.set(0, 0, 0);
        this.normal.set(0, 1, 0);
        this.surfaceTag = null;
    }
}

/**
 * Cached spatial-query results for one first-person controller. Populated
 * by {@link FirstPersonSensorsSystem} once per fixed step, read by
 * abilities and (where useful) by the base controller.
 *
 * The point of cacheing is dedup: mantle and ledge-grab both want a
 * "forward + up obstacle probe". Compute once, both read the cached hit.
 *
 * Sensor slots cleared each tick before population. Abilities should
 * treat any `hit === false` slot as "absent" — never read distance/point
 * when hit is false (they're stale or zero, depending on clear-mode).
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class FirstPersonSensors {
    constructor() {
        /** Wall to the body-local left, probed at chest height. */
        this.wallLeft = new SensorHit();
        /** Wall to the body-local right, probed at chest height. */
        this.wallRight = new SensorHit();
        /** Wall directly in front of the body. */
        this.wallFront = new SensorHit();
        /**
         * Obstacle ahead at chest/waist height — used by mantle/vault.
         * Distance is along the body's forward direction; normal is the
         * face of the obstacle the player is approaching.
         */
        this.obstacleAhead = new SensorHit();
        /**
         * Ledge edge ahead — for ledge grab. Populated when the forward
         * obstacle probe hits and a downward probe just past the
         * obstacle's top finds empty space, indicating a grabbable edge.
         */
        this.ledgeAhead = new SensorHit();
        /**
         * True when `ledgeAhead`'s top surface is wide enough to STAND on
         * (a standing capsule fits forward of the edge), false when it's a
         * knife-edge / thin wall you could only hang from. Mantle reads this
         * to refuse vaulting onto a top it would immediately fall off; ledge
         * grab ignores it (you can hang from a thin edge). Default false so a
         * mantle never fires on stale data.
         */
        this.ledgeStandable = false;
    }

    /** Clear all sensor slots — call at the start of each tick. */
    clearAll() {
        this.wallLeft.clear();
        this.wallRight.clear();
        this.wallFront.clear();
        this.obstacleAhead.clear();
        this.ledgeAhead.clear();
        this.ledgeStandable = false;
    }
}
