import { Script } from "playcanvas";
import { ViverseApp, Debugger } from './toolkit/core.mjs';
import { TriggerDispatcher, EntityTriggerEnterTrigger, EntityTriggerLeaveTrigger, EntityCollisionStartTrigger, EntityCollisionEndTrigger, EntityAnimStartTrigger, EntityAnimEndTrigger, EntityAnimCustomEventTrigger, SubscribeNotificationTrigger, EntitySelectedTrigger, InteractionExecutingTrigger, SeatOccupiedTrigger, SeatVacatedTrigger, } from './toolkit/extension.mjs';
import { TriggerEventType, TriggerCollisionEvent, TriggerCollisionFilter, TriggerAnimEvent, TriggerSeatState, } from './toolkit/types.mjs';
/** @enum {string} */
const Type = {
    OnSelect: 'onSelect',
    OnCollision: 'onCollision',
    OnAnimation: 'onAnimation',
    OnNotificationEvent: 'onNotificationEvent',
    OnInteractionExecuting: 'onInteractionExecuting',
    OnSeatStateChanged: 'onSeatStateChanged',
};
/** @enum {string} */
const CollisionEvent = {
    TriggerEnter: 'triggerenter',
    TriggerLeave: 'triggerleave',
    CollisionStart: 'collisionstart',
    CollisionEnd: 'collisionend',
};
/** @enum {string} */
const CollisionFilter = {
    Any: 'any',
    Player: 'player',
    Tag: 'tag',
    PlayerAndTag: 'playerAndTag',
};
/** @enum {string} */
const AnimEvent = {
    Start: 'start',
    End: 'end',
    CustomEvent: 'customEvent',
};
/** @enum {string} */
const SeatState = {
    Occupied: 'occupied',
    Vacated: 'vacated',
};
/** @interface */
class Trigger {
    /**
     * Unique identifier for this trigger. Used to reference it
     * inside conditions or actions.
     * @attribute
     * @title Name
     * @type {string}
     */
    name = '';
    /**
     * The type of trigger event that activates this trigger.
     * @attribute
     * @title Type
     * @type {Type}
     */
    type = Type.OnSelect;
    /**
     * Minimum time interval (in seconds) before this trigger
     * can activate again. Calls made faster than this interval
     * will be ignored.
     * @attribute
     * @title Cooldown Time
     * @type {number}
     * @range [0]
     */
    cooldown = 0;
    /**
     * Collision event type that activates this trigger.
     * @attribute
     * @title Collision Event
     * @type {CollisionEvent}
     * @visibleif {type === 'onCollision'}
     */
    collisionEvent = CollisionEvent.TriggerEnter;
    /**
     * The collision filter to apply when detecting collision events.
     * This can be used to only detect collisions with the player,
     * or with entities that have specific tags.
     * @attribute
     * @title Collision Filter
     * @type {CollisionFilter}
     * @visibleif {type === 'onCollision'}
     */
    collisionFilter = CollisionFilter.Player;
    /**
     * Tags filter to further filter collision events.
     * Entities with at least one of the specified tags can be triggered.
     * If set to empty array, nothing will pass the filter.
     * @attribute
     * @title Tags Filter
     * @type {string[]}
     * @visibleif {(type === 'onCollision' && (collisionFilter === 'tag' || collisionFilter === 'playerAndTag'))}
     */
    tagsFilter = [];
    /**
     * Animation event type that activates this trigger.
     * @attribute
     * @title Animation Event
     * @type {AnimEvent}
     * @visibleif {type === 'onAnimation'}
     */
    animEvent = AnimEvent.Start;
    /**
     * The Animation event name that activates this trigger.
     * @attribute
     * @title Event Name
     * @type {string}
     * @visibleif {type === 'onAnimation' && animEvent === 'customEvent'}
     */
    animCustomEventName = '';
    /**
     * The Notification event name that activates this trigger.
     * @attribute
     * @title Event Name
     * @type {string}
     * @visibleif {type === 'onNotificationEvent'}
     */
    notificationName = '';
    /**
     * The seat state that activates this trigger.
     * @attribute
     * @title Seat State
     * @type {SeatState}
     * @visibleif {type === 'onSeatStateChanged'}
     */
    seatState = SeatState.Occupied;
    /**
     * Collisions from entities with these tags should not block selection.
     * Other entities behind them can still be detected.
     * @attribute
     * @title Exclude Tags
     * @type {string[]}
     * @visibleif {type === 'onSelect'}
     */
    selectionExcludeTags = [];
}
export class ViverseTrigger extends Script {
    static scriptName = 'viverseTrigger';
    /**
     * Configure multiple triggers for this entity. The array size is controlled by Trigger Count above.
     * @attribute
     * @title Trigger List
     * @type {Trigger[]}
     */
    triggers = [];
    _triggerMap = new Map();
    _dispatcher = TriggerDispatcher.getInstance();
    initialize() {
        if (!this.triggers || this.triggers.length <= 0)
            return;
        this.triggers.forEach((trigger) => {
            this._dispatcher.addTriggerEvent(trigger.name);
        });
        this._checkDuplicateTriggerNames();
        this._resolveTriggerMap();
        this.entity.on('destroy', () => {
            this._triggerMap.forEach((triggerInstance) => triggerInstance.destroy());
            this._triggerMap.clear();
            this.triggers.forEach((trigger) => {
                this._dispatcher.removeTriggerEvent(trigger.name);
            });
        });
    }
    _checkDuplicateTriggerNames() {
        this.triggers.forEach((trigger) => {
            if (this._dispatcher.hasDuplicateTriggerEvent(trigger.name)) {
                Debugger.warn(`[ViverseTrigger] Duplicate trigger name '${trigger.name}' found on entity '${this.entity.name}'. Trigger names must be unique to prevent unexpected behavior.`);
            }
        });
    }
    _resolveTriggerMap() {
        const viverseApp = ViverseApp.getApplication();
        if (!viverseApp) {
            Debugger.error('[ViverseTrigger] ViverseApp is not initialized.');
            return;
        }
        this.triggers.forEach((trigger) => {
            let triggerInstance = null;
            if (trigger.type === Type.OnCollision) {
                const config = {
                    entity: this.entity,
                    cooldown: trigger.cooldown,
                    ...((trigger.collisionFilter === CollisionFilter.Tag ||
                        trigger.collisionFilter === CollisionFilter.PlayerAndTag) && {
                        tagsFilter: trigger.tagsFilter,
                    }),
                    filterByPlayer: trigger.collisionFilter === CollisionFilter.Player ||
                        trigger.collisionFilter === CollisionFilter.PlayerAndTag,
                };
                if (trigger.collisionEvent === CollisionEvent.TriggerEnter) {
                    triggerInstance = new EntityTriggerEnterTrigger(viverseApp, config);
                }
                else if (trigger.collisionEvent === CollisionEvent.TriggerLeave) {
                    triggerInstance = new EntityTriggerLeaveTrigger(viverseApp, config);
                }
                else if (trigger.collisionEvent === CollisionEvent.CollisionStart) {
                    triggerInstance = new EntityCollisionStartTrigger(viverseApp, config);
                }
                else if (trigger.collisionEvent === CollisionEvent.CollisionEnd) {
                    triggerInstance = new EntityCollisionEndTrigger(viverseApp, config);
                }
            }
            else if (trigger.type === Type.OnAnimation) {
                const config = {
                    entity: this.entity,
                    cooldown: trigger.cooldown,
                };
                if (trigger.animEvent === AnimEvent.Start) {
                    triggerInstance = new EntityAnimStartTrigger(viverseApp, config);
                }
                else if (trigger.animEvent === AnimEvent.End) {
                    triggerInstance = new EntityAnimEndTrigger(viverseApp, config);
                }
                else if (trigger.animEvent === AnimEvent.CustomEvent) {
                    triggerInstance = new EntityAnimCustomEventTrigger(viverseApp, {
                        ...config,
                        eventName: trigger.animCustomEventName.trim(),
                    });
                }
            }
            else if (trigger.type === Type.OnNotificationEvent) {
                const config = {
                    cooldown: trigger.cooldown,
                    notificationName: trigger.notificationName,
                };
                triggerInstance = new SubscribeNotificationTrigger(viverseApp, config);
            }
            else if (trigger.type === Type.OnSelect) {
                const config = {
                    entity: this.entity,
                    cooldown: trigger.cooldown,
                    selectionExcludeTags: trigger.selectionExcludeTags,
                };
                triggerInstance = new EntitySelectedTrigger(viverseApp, config);
            }
            else if (trigger.type === Type.OnInteractionExecuting) {
                const config = {
                    cooldown: trigger.cooldown,
                };
                triggerInstance = new InteractionExecutingTrigger(viverseApp, config);
            }
            else if (trigger.type === Type.OnSeatStateChanged) {
                const config = {
                    entity: this.entity,
                    cooldown: trigger.cooldown,
                };
                if (trigger.seatState === SeatState.Occupied) {
                    triggerInstance = new SeatOccupiedTrigger(viverseApp, config);
                }
                else if (trigger.seatState === SeatState.Vacated) {
                    triggerInstance = new SeatVacatedTrigger(viverseApp, config);
                }
            }
            if (triggerInstance !== null) {
                triggerInstance.on('triggered', () => {
                    this._fireTriggerEvent(trigger.name);
                });
                this._triggerMap.set(trigger, triggerInstance);
            }
        });
    }
    _fireTriggerEvent(name) {
        this._dispatcher.fireTriggerEvent(name);
    }
}
