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 { Layers } from "three";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { getParam } from "../engine/engine_utils.js";
import { BoxHelperComponent } from "./BoxHelperComponent.js";
import { Behaviour, GameObject } from "./Component.js";
import { EventList } from "./EventList.js";
const debug = getParam("debugspatialtrigger");
/** Layer instances used for mask comparison */
const layer1 = new Layers();
const layer2 = new Layers();
/**
 * Tests if two layer masks intersect
 * @param mask1 First layer mask
 * @param mask2 Second layer mask
 * @returns True if the layers intersect
 */
function testMask(mask1, mask2) {
    layer1.mask = mask1;
    layer2.mask = mask2;
    return layer1.test(layer2);
}
/**
 * Component that receives and responds to spatial events, like entering or exiting a trigger zone.
 * Used in conjunction with {@link SpatialTrigger} to create interactive spatial events.
 *
 * Place this on objects that should react when entering trigger zones. The receiver checks
 * against all active SpatialTriggers each frame and fires events when intersections change.
 *
 * Events can be connected via {@link EventList} in the editor or listened to in code.
 *
 * @example Listen to trigger events
 * ```ts
 * export class DoorTrigger extends Behaviour {
 *   @serializable(SpatialTriggerReceiver)
 *   receiver?: SpatialTriggerReceiver;
 *
 *   start() {
 *     this.receiver?.onEnter?.addEventListener(() => {
 *       console.log("Player entered door zone");
 *     });
 *   }
 * }
 * ```
 *
 * @summary Receives spatial trigger events
 * @category Interactivity
 * @group Components
 * @see {@link SpatialTrigger} for defining trigger zones
 * @see {@link EventList} for event handling
 * @link https://engine.needle.tools/samples/spatial-triggers/
 */
export class SpatialTriggerReceiver extends Behaviour {
    /**
     * Bitmask determining which triggers this receiver responds to
     * Only triggers with matching masks will interact with this receiver
     */
    triggerMask = 0;
    /** Event invoked when this object enters a trigger zone */
    onEnter;
    /** Event invoked continuously while this object is inside a trigger zone */
    onStay;
    /** Event invoked when this object exits a trigger zone */
    onExit;
    /**
     * Initializes the receiver and logs debug info if enabled
     * @internal
     */
    start() {
        if (debug)
            console.log(this.name, this.triggerMask, this);
    }
    /**
     * Checks for intersections with spatial triggers and fires appropriate events
     * Handles enter, stay, and exit events for all relevant triggers
     * @internal
     */
    update() {
        this.currentIntersected.length = 0;
        for (const trigger of SpatialTrigger.triggers) {
            if (testMask(trigger.triggerMask, this.triggerMask)) {
                if (trigger.test(this.gameObject)) {
                    this.currentIntersected.push(trigger);
                }
            }
        }
        for (let i = this.lastIntersected.length - 1; i >= 0; i--) {
            const last = this.lastIntersected[i];
            if (this.currentIntersected.indexOf(last) < 0) {
                this.onExitTrigger(last);
                this.lastIntersected.splice(i, 1);
            }
        }
        for (const cur of this.currentIntersected) {
            if (this.lastIntersected.indexOf(cur) < 0)
                this.onEnterTrigger(cur);
            this.onStayTrigger(cur);
        }
        this.lastIntersected.length = 0;
        this.lastIntersected.push(...this.currentIntersected);
    }
    /** Array of triggers currently intersecting with this receiver */
    currentIntersected = [];
    /** Array of triggers that intersected with this receiver in the previous frame */
    lastIntersected = [];
    /**
     * Handles trigger enter events.
     * @param trigger The spatial trigger that was entered
     */
    onEnterTrigger(trigger) {
        if (debug)
            console.log("ENTER TRIGGER", this.name, trigger.name, this, trigger);
        trigger.raiseOnEnterEvent(this);
        this.onEnter?.invoke();
    }
    /**
     * Handles trigger exit events.
     * @param trigger The spatial trigger that was exited
     */
    onExitTrigger(trigger) {
        if (debug)
            console.log("EXIT TRIGGER", this.name, trigger.name);
        trigger.raiseOnExitEvent(this);
        this.onExit?.invoke();
    }
    /**
     * Handles trigger stay events.
     * @param trigger The spatial trigger that the receiver is staying in
     */
    onStayTrigger(trigger) {
        trigger.raiseOnStayEvent(this);
        this.onStay?.invoke();
    }
}
__decorate([
    serializable()
], SpatialTriggerReceiver.prototype, "triggerMask", void 0);
__decorate([
    serializable(EventList)
], SpatialTriggerReceiver.prototype, "onEnter", void 0);
__decorate([
    serializable(EventList)
], SpatialTriggerReceiver.prototype, "onStay", void 0);
__decorate([
    serializable(EventList)
], SpatialTriggerReceiver.prototype, "onExit", void 0);
/**
 * A spatial trigger component that detects objects within a box-shaped area.
 * Used to trigger events when objects enter, stay in, or exit the defined area.
 *
 * The trigger area is defined by the GameObject's bounding box (uses {@link BoxHelperComponent}).
 * Objects with {@link SpatialTriggerReceiver} components are tested against this area.
 *
 * **Mask system:** Both trigger and receiver have a `triggerMask` - they only interact
 * when their masks have overlapping bits set. This allows selective triggering.
 *
 * **Debug:** Use `?debugspatialtrigger` URL parameter to visualize trigger zones.
 *
 * @example Create a pickup zone
 * ```ts
 * // On the pickup zone object
 * const trigger = pickupZone.addComponent(SpatialTrigger);
 * trigger.triggerMask = 1; // Layer 1 for pickups
 *
 * // On the player
 * const receiver = player.addComponent(SpatialTriggerReceiver);
 * receiver.triggerMask = 1; // Match the pickup layer
 * ```
 *
 * @summary Define a trigger zone that detects entering objects
 * @category Interactivity
 * @group Components
 * @see {@link SpatialTriggerReceiver} for objects that respond to triggers
 * @see {@link BoxHelperComponent} for the underlying box used to define the trigger area
 * @link https://engine.needle.tools/samples/spatial-triggers/
 */
export class SpatialTrigger extends Behaviour {
    /** Global registry of all active spatial triggers in the scene */
    static triggers = [];
    /**
     * Bitmask determining which receivers this trigger affects.
     * Only receivers with matching masks will be triggered.
     */
    // currently Layers in unity but maybe this should be a string or plane number? Or should it be a bitmask to allow receivers use multiple triggers?
    triggerMask;
    /** Box helper component used to visualize and calculate the trigger area */
    boxHelper;
    /**
     * Initializes the trigger and logs debug info if enabled
     */
    start() {
        if (debug)
            console.log(this.name, this.triggerMask, this);
    }
    /**
     * Registers this trigger in the global registry and sets up debug visualization if enabled
     */
    onEnable() {
        SpatialTrigger.triggers.push(this);
        if (!this.boxHelper) {
            this.boxHelper = GameObject.addComponent(this.gameObject, BoxHelperComponent);
            this.boxHelper?.showHelper(null, debug);
        }
    }
    /**
     * Removes this trigger from the global registry when disabled
     */
    onDisable() {
        SpatialTrigger.triggers.splice(SpatialTrigger.triggers.indexOf(this), 1);
    }
    /**
     * Tests if an object is inside this trigger's box
     * @param obj The object to test against this trigger
     * @returns True if the object is inside the trigger box
     */
    test(obj) {
        if (!this.boxHelper)
            return false;
        return this.boxHelper.isInBox(obj) ?? false;
    }
    // private args: SpatialTriggerEventArgs = new SpatialTriggerEventArgs();
    /**
     * Raises the onEnter event on any SpatialTriggerReceiver components attached to this trigger's GameObject
     * @param rec The receiver that entered this trigger
     */
    raiseOnEnterEvent(rec) {
        // this.args.trigger = this;
        // this.args.source = rec;
        GameObject.foreachComponent(this.gameObject, c => {
            if (c === rec)
                return;
            if (c instanceof SpatialTriggerReceiver) {
                c.onEnterTrigger(this);
            }
        }, false);
    }
    /**
     * Raises the onStay event on any SpatialTriggerReceiver components attached to this trigger's GameObject
     * @param rec The receiver that is staying in this trigger
     */
    raiseOnStayEvent(rec) {
        // this.args.trigger = this;
        // this.args.source = rec;
        GameObject.foreachComponent(this.gameObject, c => {
            if (c === rec)
                return;
            if (c instanceof SpatialTriggerReceiver) {
                c.onStayTrigger(this);
            }
        }, false);
    }
    /**
     * Raises the onExit event on any SpatialTriggerReceiver components attached to this trigger's GameObject
     * @param rec The receiver that exited this trigger
     */
    raiseOnExitEvent(rec) {
        GameObject.foreachComponent(this.gameObject, c => {
            if (c === rec)
                return;
            if (c instanceof SpatialTriggerReceiver) {
                c.onExitTrigger(this);
            }
        }, false);
    }
}
__decorate([
    serializable()
], SpatialTrigger.prototype, "triggerMask", void 0);
//# sourceMappingURL=SpatialTrigger.js.map