export class BehaviorTreeNode {
    name;
    status = "failure";
    running = false;
    constructor(name) {
        this.name = name;
    }
    reset() {
        this.status = "failure";
        this.running = false;
    }
}
export class Blackboard {
    parent;
    values = new Map();
    changes = [];
    localVersion = 0;
    constructor(parent) {
        this.parent = parent;
    }
    get version() {
        return this.localVersion;
    }
    set(key, value) {
        const previous = this.get(key);
        this.localVersion += 1;
        this.values.set(key, { value, version: this.localVersion });
        this.changes.push({ key, value, previous, version: this.localVersion });
    }
    get(key, fallback) {
        const local = this.values.get(key);
        if (local)
            return local.value;
        if (this.parent?.has(key))
            return this.parent.get(key, fallback);
        return fallback;
    }
    has(key) {
        return this.values.has(key) || this.parent?.has(key) === true;
    }
    snapshot() {
        const inherited = this.parent?.snapshot() ?? {};
        return { ...inherited, ...Object.fromEntries([...this.values].map(([key, entry]) => [key, entry.value])) };
    }
    changeLog() {
        return [...this.changes];
    }
}
export class BehaviorAction extends BehaviorTreeNode {
    action;
    constructor(name, action) {
        super(name);
        this.action = action;
    }
    tick(context) {
        this.running = true;
        this.status = this.action(context);
        if (this.status !== "running")
            this.running = false;
        return this.status;
    }
}
export class BehaviorCondition extends BehaviorTreeNode {
    condition;
    constructor(name, condition) {
        super(name);
        this.condition = condition;
    }
    tick(context) {
        this.status = this.condition(context) ? "success" : "failure";
        return this.status;
    }
}
export class BehaviorSequence extends BehaviorTreeNode {
    children;
    currentIndex = 0;
    constructor(name, children) {
        super(name);
        this.children = children;
    }
    tick(context) {
        this.running = true;
        while (this.currentIndex < this.children.length) {
            const child = this.children[this.currentIndex];
            const status = child.tick({ ...context, depth: context.depth + 1 });
            if (status === "failure") {
                this.reset();
                return "failure";
            }
            if (status === "running") {
                this.status = "running";
                return "running";
            }
            this.currentIndex += 1;
        }
        this.reset();
        this.status = "success";
        return "success";
    }
    reset() {
        super.reset();
        this.currentIndex = 0;
        for (const child of this.children)
            child.reset();
    }
}
export class BehaviorSelector extends BehaviorTreeNode {
    children;
    currentIndex = 0;
    constructor(name, children) {
        super(name);
        this.children = children;
    }
    tick(context) {
        this.running = true;
        while (this.currentIndex < this.children.length) {
            const child = this.children[this.currentIndex];
            const status = child.tick({ ...context, depth: context.depth + 1 });
            if (status === "success") {
                this.reset();
                return "success";
            }
            if (status === "running") {
                this.status = "running";
                return "running";
            }
            this.currentIndex += 1;
        }
        this.reset();
        this.status = "failure";
        return "failure";
    }
    reset() {
        super.reset();
        this.currentIndex = 0;
        for (const child of this.children)
            child.reset();
    }
}
export class BehaviorTree {
    root;
    blackboard;
    tickCounter = 0;
    lastTrace = [];
    constructor(root, blackboard = new Blackboard()) {
        this.root = root;
        this.blackboard = blackboard;
    }
    tick(deltaSeconds) {
        this.tickCounter += 1;
        this.lastTrace = [];
        const status = this.tickNode(this.root, { blackboard: this.blackboard, deltaSeconds, depth: 0 });
        return {
            status,
            tickCount: this.tickCounter,
            trace: [...this.lastTrace],
            blackboardVersion: this.blackboard.version
        };
    }
    reset() {
        this.root.reset();
        this.lastTrace = [];
        this.tickCounter = 0;
    }
    tickNode(node, context) {
        const status = node.tick({
            ...context,
            blackboard: new TracingBlackboard(context.blackboard, (key, value) => {
                this.lastTrace.push(`${node.name}:${key}=${String(value)}`);
            })
        });
        this.lastTrace.push(`${node.name}:${status}`);
        return status;
    }
}
class TracingBlackboard extends Blackboard {
    target;
    onSet;
    constructor(target, onSet) {
        super();
        this.target = target;
        this.onSet = onSet;
    }
    get version() {
        return this.target.version;
    }
    set(key, value) {
        this.target.set(key, value);
        this.onSet(key, value);
    }
    get(key, fallback) {
        return this.target.get(key, fallback);
    }
    has(key) {
        return this.target.has(key);
    }
    snapshot() {
        return this.target.snapshot();
    }
    changeLog() {
        return this.target.changeLog();
    }
}
//# sourceMappingURL=BehaviorTree.js.map