import { AbstractBlackboard } from "./AbstractBlackboard.js";

/**
 * Cascading blackboard interface. Allows composition of multiple blackboard to be used to represent a single whole
 * The access is always done from top to bottom. If a value is not found at the top of the stack, a lower slot will be considered until the end. In which case value will be created in the blackboard identified by "write_slot"
 */
export class BlackboardStack extends AbstractBlackboard {
    constructor() {
        super();

        /**
         * @private
         * @type {AbstractBlackboard[]}
         */
        this.__items = [];

        /**
         *
         * @type {AbstractBlackboard}
         * @private
         */
        this.__write_slot = null;
    }

    contains(name, type) {
        const items = this.__items;
        const n = items.length;
        for (let i = 0; i < n; i++) {
            const blackboard = items[i];

            if (blackboard.contains(name, type)) {
                return true;
            }
        }

        return false;
    }

    acquire(name, type, initialValue) {
        // check for value at each level of the stack starting from the top

        const items = this.__items;
        const stack_length = items.length;

        for (let i = stack_length - 1; i >= 0; i--) {
            const blackboard = items[i];

            if (blackboard.contains(name, type)) {
                return blackboard.acquire(name, type, initialValue);
            }
        }

        // not found, use write slot
        return this.__write_slot.acquire(name, type, initialValue);
    }

    /**
     *
     * @param {AbstractBlackboard} v
     */
    setWriteSlot(v) {
        this.__write_slot = v;
    }

    __pick_write_slot() {
        const length = this.__items.length;

        if (length === 0) {
            this.setWriteSlot(null);
        } else {

            const blackboard = this.__items[length - 1];

            this.setWriteSlot(blackboard);

        }
    }

    /**
     *
     * @param {AbstractBlackboard} item
     */
    push(item) {
        this.__items.push(item);

        if (this.__write_slot === null) {
            // update write slot
            this.__pick_write_slot();
        }
    }

    /**
     *
     * @param {AbstractBlackboard} item
     * @param {number} position
     */
    insert(item, position) {
        this.__items.splice(position, 0, item);

        if (this.__write_slot === null) {
            // update write slot
            this.__pick_write_slot();
        }
    }

    /**
     *
     * @param {AbstractBlackboard} item
     * @returns {boolean}
     */
    remove(item) {
        const i = this.__items.indexOf(item);

        if (i === -1) {
            return false;
        }

        this.__items.splice(i, 1);

        if (this.__write_slot === item) {
            // update write slot
            this.__pick_write_slot();
        }

        return true;
    }
}
