import Clock from "../../../Clock.js";
import { System } from "../../../ecs/System.js";
import { BehaviorStatus } from "../BehaviorStatus.js";
import { BehaviorComponent } from "./BehaviorComponent.js";
import { BehaviorComponentFlag } from "./BehaviorComponentFlag.js";
import { ClockChannelType } from "./ClockChannelType.js";

/**
 *
 * @param {Behavior} behavior
 * @param {number} timeDelta
 * @returns {BehaviorStatus}
 */
function updateBehavior(behavior, timeDelta) {

    try {
        return behavior.tick(timeDelta);
    } catch (e) {
        console.error('Behavior threw an exception', e);

        // Transition behavior into failed state
        return BehaviorStatus.Failed;
    }
}

/**
 * @extends System<BehaviorComponent>
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class BehaviorSystem extends System {
    /**
     *
     * @param {Engine} engine
     */
    constructor(engine) {
        super();


        this.dependencies = [BehaviorComponent];

        this.systemClock = new Clock();

        /**
         *
         * @type {Engine}
         */
        this.engine = engine;
    }

    async startup(entityManager) {
        this.systemClock.start();

        this.entityManager = entityManager;
    }

    async shutdown(entityManager) {
        this.systemClock.stop();
    }

    /**
     *
     * @param {BehaviorComponent} component
     * @param {number} entity
     */
    link(component, entity) {
        const behavior = component.behavior;
        component.clearFlag(BehaviorComponentFlag.Resolved);

        if (behavior !== null) {

            // initialize behavior
            const dataset = this.entityManager.dataset;

            try {
                behavior.initialize({ entity, ecd: dataset, engine: this.engine });
            } catch (e) {
                console.warn('Behavior initialization failed:', e);
            }
        }
    }

    /**
     *
     * @param {BehaviorComponent} component
     * @param {number} entity
     */
    unlink(component, entity) {
        const behavior = component.behavior;

        if (behavior !== null) {

            // finalize behavior
            behavior.finalize();
        }
    }

    update(timeDelta) {
        const systemDelta = this.systemClock.getDelta();

        const dataset = this.entityManager.dataset;

        if (dataset === null) {
            //no data
            return;
        }

        /**
         *
         * @param {BehaviorComponent} b
         * @param {number} entity
         */
        function visitBehavior(b, entity) {
            if (b.getFlag(BehaviorComponentFlag.Resolved)) {
                return;
            }

            let td;

            if (b.clock === ClockChannelType.Simulation) {
                //use simulation time
                td = timeDelta;
            } else if (b.clock === ClockChannelType.System) {
                //use system clock time delta
                td = systemDelta;
            }

            const behavior = b.behavior;

            if (behavior !== null) {
                const state = updateBehavior(behavior, td);

                if (state === BehaviorStatus.Failed || state === BehaviorStatus.Succeeded) {
                    b.setFlag(BehaviorComponentFlag.Resolved);
                }
            }

        }

        dataset.traverseComponents(BehaviorComponent, visitBehavior);
    }
}
