"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decoder = void 0;
const tslib_1 = require("tslib");
const nodes = tslib_1.__importStar(require("../../../nodes"));
const fromBase64_1 = require("@jsonjoy.com/base64/lib/fromBase64");
const clock_1 = require("../../../../json-crdt-patch/clock");
const model_1 = require("../../../model");
class Decoder {
    decode({ time, root }) {
        const isServerClock = typeof time === 'number';
        const doc = isServerClock ? model_1.Model.withServerClock(void 0, time) : model_1.Model.create(void 0, this.cClock(time));
        this.cRoot(doc, root);
        return doc;
    }
    cClock(timestamps) {
        const [stamp] = timestamps;
        const vectorClock = new clock_1.ClockVector(stamp[0], stamp[1]);
        const length = timestamps.length;
        for (let i = 1; i < length; i++) {
            const stamp = timestamps[i];
            const [sessionId, time] = stamp;
            vectorClock.observe((0, clock_1.ts)(sessionId, time), 1);
        }
        return vectorClock;
    }
    cTs(stamp) {
        const isServerClock = typeof stamp === 'number';
        return isServerClock ? (0, clock_1.ts)(1 /* SESSION.SERVER */, stamp) : (0, clock_1.ts)(stamp[0], stamp[1]);
    }
    cRoot(doc, { value }) {
        const val = value ? this.cNode(doc, value) : new nodes.ConNode(doc.clock.tick(0), null);
        const root = new nodes.RootNode(doc, val.id);
        doc.root = root;
    }
    cNode(doc, node) {
        switch (node.type) {
            case 'obj':
                return this.cObj(doc, node);
            case 'arr':
                return this.cArr(doc, node);
            case 'str':
                return this.cStr(doc, node);
            case 'val':
                return this.cVal(doc, node);
            case 'con':
                return this.cCon(doc, node);
            case 'vec':
                return this.cVec(doc, node);
            case 'bin':
                return this.cBin(doc, node);
        }
        throw new Error('UNKNOWN_NODE');
    }
    cObj(doc, node) {
        const id = this.cTs(node.id);
        const obj = new nodes.ObjNode(doc, id);
        const map = node.map;
        const keys = Object.keys(map);
        for (const key of keys) {
            const keyNode = map[key];
            obj.put(key, this.cNode(doc, keyNode).id);
        }
        doc.index.set(id, obj);
        return obj;
    }
    cVec(doc, node) {
        const id = this.cTs(node.id);
        const obj = new nodes.VecNode(doc, id);
        const elements = obj.elements;
        const map = node.map;
        const length = map.length;
        for (let i = 0; i < length; i++) {
            const component = map[i];
            if (!component)
                elements.push(undefined);
            else
                elements.push(this.cNode(doc, component).id);
        }
        doc.index.set(id, obj);
        return obj;
    }
    cArr(doc, node) {
        const id = this.cTs(node.id);
        const rga = new nodes.ArrNode(doc, id);
        const chunks = node.chunks;
        const length = chunks.length;
        if (length) {
            let i = 0;
            rga.ingest(length, () => {
                const c = chunks[i++];
                const id = this.cTs(c.id);
                if (typeof c.span === 'number')
                    return new nodes.ArrChunk(id, c.span, undefined);
                else {
                    const ids = c.value.map((n) => this.cNode(doc, n).id);
                    return new nodes.ArrChunk(id, ids.length, ids);
                }
            });
        }
        doc.index.set(id, rga);
        return rga;
    }
    cStr(doc, node) {
        const id = this.cTs(node.id);
        const rga = new nodes.StrNode(id);
        const chunks = node.chunks;
        const length = chunks.length;
        if (length) {
            let i = 0;
            rga.ingest(length, () => {
                const c = chunks[i++];
                const id = this.cTs(c.id);
                if (typeof c.span === 'number')
                    return new nodes.StrChunk(id, c.span, '');
                else {
                    const value = c.value;
                    return new nodes.StrChunk(id, value.length, value);
                }
            });
        }
        doc.index.set(id, rga);
        return rga;
    }
    cBin(doc, node) {
        const id = this.cTs(node.id);
        const rga = new nodes.BinNode(id);
        const chunks = node.chunks;
        const length = chunks.length;
        if (length) {
            let i = 0;
            rga.ingest(length, () => {
                const c = chunks[i++];
                const id = this.cTs(c.id);
                if (typeof c.span === 'number')
                    return new nodes.BinChunk(id, c.span, undefined);
                else {
                    const value = c.value;
                    const buf = (0, fromBase64_1.fromBase64)(value);
                    return new nodes.BinChunk(id, buf.length, buf);
                }
            });
        }
        doc.index.set(id, rga);
        return rga;
    }
    cVal(doc, node) {
        const id = this.cTs(node.id);
        const val = this.cNode(doc, node.value);
        const obj = new nodes.ValNode(doc, id, val.id);
        doc.index.set(id, obj);
        return obj;
    }
    cCon(doc, node) {
        const id = this.cTs(node.id);
        const val = node.timestamp ? this.cTs(node.value) : node.value;
        const obj = new nodes.ConNode(id, val);
        doc.index.set(id, obj);
        return obj;
    }
}
exports.Decoder = Decoder;
//# sourceMappingURL=Decoder.js.map