import { printTree } from 'tree-dump/lib/printTree';
import { compare, printTs } from '../../../json-crdt-patch/clock';
import { ConNode } from '../const/ConNode';
/**
 * Represents a `obj` JSON CRDT node, which is a Last-write-wins (LWW) object.
 * It is a map of string keys to LWW registers. The value of each register is
 * a reference to another JSON CRDT node.
 *
 * @category CRDT Node
 */
export class ObjNode {
    doc;
    id;
    /**
     * @ignore
     */
    keys = new Map();
    constructor(
    /**
     * @ignore
     */
    doc, id) {
        this.doc = doc;
        this.id = id;
    }
    /**
     * Retrieves a JSON CRDT node at the given key.
     *
     * @param key A key of the object.
     * @returns JSON CRDT node at the given key, if any.
     */
    get(key) {
        const id = this.keys.get(key);
        if (!id)
            return undefined;
        return this.doc.index.get(id);
    }
    /**
     * Rewrites object key.
     *
     * @param key Object key to set.
     * @param id ID of the contents of the key.
     * @returns Returns old entry ID, if any.
     * @ignore
     */
    put(key, id) {
        const currentId = this.keys.get(key);
        if (currentId && compare(currentId, id) >= 0)
            return;
        this.keys.set(key, id);
        return currentId;
    }
    /**
     * Iterate over all key-value pairs in the object.
     *
     * @param callback Callback to call for each key-value pair.
     */
    nodes(callback) {
        const index = this.doc.index;
        this.keys.forEach((id, key) => callback(index.get(id), key));
    }
    forEach(callback) {
        const index = this.doc.index;
        this.keys.forEach((id, key) => {
            const value = index.get(id);
            if (!value || (value instanceof ConNode && value.val === void 0))
                return;
            callback(key, value);
        });
    }
    // ----------------------------------------------------------------- JsonNode
    /**
     * @ignore
     */
    children(callback) {
        const index = this.doc.index;
        this.keys.forEach((id, key) => callback(index.get(id)));
    }
    /**
     * @ignore
     */
    child() {
        return undefined;
    }
    /**
     * @ignore
     */
    container() {
        return this;
    }
    /**
     * @ignore
     */
    _tick = 0;
    /**
     * @ignore
     */
    _view = {};
    /**
     * @ignore
     */
    view() {
        const doc = this.doc;
        const tick = doc.clock.time + doc.tick;
        const _view = this._view;
        if (this._tick === tick)
            return _view;
        const view = {};
        const index = doc.index;
        let useCache = true;
        this.keys.forEach((id, key) => {
            const valueNode = index.get(id);
            if (!valueNode) {
                useCache = false;
                return;
            }
            const value = valueNode.view();
            if (value !== undefined) {
                if (_view[key] !== value)
                    useCache = false;
                view[key] = value;
            }
            else if (_view[key] !== undefined)
                useCache = false;
        });
        return useCache ? _view : ((this._tick = tick), (this._view = view));
    }
    /**
     * @ignore
     */
    api = undefined;
    name() {
        return 'obj';
    }
    // ---------------------------------------------------------------- Printable
    toString(tab = '') {
        const header = this.name() + ' ' + printTs(this.id);
        return (header +
            printTree(tab, [...this.keys.entries()]
                .filter(([, id]) => !!this.doc.index.get(id))
                .map(([key, id]) => (tab) => JSON.stringify(key) + printTree(tab + ' ', [(tab) => this.doc.index.get(id).toString(tab)]))));
    }
}
//# sourceMappingURL=ObjNode.js.map