import { Script } from "playcanvas";
import { Debugger } from './toolkit/core.mjs';
import { TriggerDispatcher } from './toolkit/extension.mjs';
/** @enum {string} */
const OperatorType = {
    And: 'and',
    Or: 'or',
};
/** @interface */
class Condition {
    /**
     * Unique identifier for this condition.
     * Actions can reference this condition by name.
     * @attribute
     * @title Name
     * @type {string}
     */
    name = '';
    /**
     * How the linked triggers are combined:
     * - **And**: all triggers must activate
     * - **Or**: at least one trigger must activate
     * @attribute
     * @title Operator
     * @type {OperatorType}
     */
    operator = OperatorType.And;
    /**
     * The first trigger in this condition.
     * @attribute
     * @title Trigger A
     * @type {string}
     */
    triggerA = '';
    /**
     * The second trigger in this condition.
     * @attribute
     * @title Trigger B
     * @type {string}
     */
    triggerB = '';
    /**
     * Optional time window (in seconds) within which
     * all triggers must occur for the condition to pass.
     * A value of 0 means no time restriction.
     * @attribute
     * @title Time Window
     */
    timeWindow = 0;
}
export class ViverseCondition extends Script {
    static scriptName = 'viverseCondition';
    /**
     * The list of conditions defined on this entity.
     * @attribute
     * @title Condition List
     * @type {Condition[]}
     */
    conditions = [];
    _conditionMap = new Map();
    _listeners = [];
    _dispatcher = TriggerDispatcher.getInstance();
    initialize() {
        if (!this.conditions || this.conditions.length <= 0)
            return;
        this.conditions.forEach((condition) => {
            this._dispatcher.addTriggerEvent(condition.name);
        });
        this._resolveListeners();
        this._resolveConditionMap();
        this._checkDuplicateConditionNames();
        this.entity.on('destroy', () => {
            this._listeners.forEach(({ activation, handler }) => {
                this._dispatcher.off(activation, handler);
            });
            this._listeners.length = 0;
            this.conditions.forEach((condition) => {
                [condition.triggerA, condition.triggerB].forEach((name) => {
                    this._dispatcher.removeTriggerEvent(name);
                });
            });
        });
    }
    _checkDuplicateConditionNames() {
        this.conditions.forEach((condition) => {
            if (this._dispatcher.hasDuplicateTriggerEvent(condition.name)) {
                Debugger.warn(`[ViverseCondition] Duplicate condition name detected: '${condition.name}'. Condition names must be unique to prevent unexpected behavior.`);
            }
        });
    }
    _resolveListeners() {
        this.conditions.forEach((condition) => {
            [condition.triggerA, condition.triggerB].forEach((name) => {
                const handler = () => this._onTriggered(name);
                const eventName = this._dispatcher.getTriggerEventName(name);
                this._dispatcher.on(eventName, handler);
                this._listeners.push({ activation: eventName, handler });
            });
        });
    }
    _resolveConditionMap() {
        this.conditions.forEach((condition) => {
            if (!this._conditionMap.has(condition)) {
                this._conditionMap.set(condition, {
                    operator: condition.operator,
                    isTriggerAComplete: false,
                    isTriggerBComplete: false,
                    get isComplete() {
                        if (condition.operator === OperatorType.And) {
                            return this.isTriggerAComplete && this.isTriggerBComplete;
                        }
                        else if (condition.operator === OperatorType.Or) {
                            return this.isTriggerAComplete || this.isTriggerBComplete;
                        }
                    },
                    timeWindow: 0,
                });
            }
        });
    }
    _onTriggered(name) {
        this.conditions.forEach((condition) => {
            const states = this._conditionMap.get(condition);
            if (!states)
                return;
            if (condition.triggerA === name)
                states.isTriggerAComplete = true;
            if (condition.triggerB === name)
                states.isTriggerBComplete = true;
        });
    }
    _fireConditionEvent(name) {
        this._dispatcher.fireTriggerEvent(name);
        this._dispatcher.removeTriggerEvent(name);
    }
    _clearTriggersComplete(states) {
        states.isTriggerAComplete = false;
        states.isTriggerBComplete = false;
    }
    _canApplyTimeWindow(condition) {
        return condition.operator === OperatorType.And && condition.timeWindow > 0;
    }
    _applyTimeWindow(states, timeWindow, dt) {
        const { isTriggerAComplete, isTriggerBComplete } = states;
        if (isTriggerAComplete !== isTriggerBComplete) {
            states.timeWindow += dt;
        }
        if (states.timeWindow > timeWindow) {
            this._clearTriggersComplete(states);
            states.timeWindow = 0;
        }
    }
    /**
     * Called every frame while the script is enabled.
     * @param {number} dt - Delta time since last frame.
     */
    update(dt) {
        this.conditions.forEach((condition) => {
            const states = this._conditionMap.get(condition);
            if (!states)
                return;
            if (this._canApplyTimeWindow(condition))
                this._applyTimeWindow(states, condition.timeWindow, dt);
            if (states.isComplete) {
                this._fireConditionEvent(condition.name);
                this._clearTriggersComplete(states);
                states.timeWindow = 0;
            }
        });
    }
}
