"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Encoder = void 0;
const tslib_1 = require("tslib");
const nodes = tslib_1.__importStar(require("../../../nodes"));
const toBase64_1 = require("@jsonjoy.com/base64/lib/toBase64");
const clock_1 = require("../../../../json-crdt-patch/clock");
class Encoder {
    encode(model) {
        this.model = model;
        const clock = model.clock;
        const isServerClock = clock.sid === 1 /* SESSION.SERVER */;
        return {
            time: isServerClock ? clock.time : this.cClock(model.clock),
            root: this.cVal(model.root),
        };
    }
    cClock(clock) {
        const data = [];
        const sessionId = clock.sid;
        const localTs = clock.peers.get(sessionId);
        if (!localTs)
            data.push([sessionId, clock.time]);
        for (const c of clock.peers.values())
            data.push([c.sid, c.time]);
        return data;
    }
    cTs(ts) {
        return ts.sid === 1 /* SESSION.SERVER */ ? ts.time : [ts.sid, ts.time];
    }
    cNode(node) {
        if (node instanceof nodes.ObjNode)
            return this.cObj(node);
        else if (node instanceof nodes.ArrNode)
            return this.cArr(node);
        else if (node instanceof nodes.StrNode)
            return this.cStr(node);
        else if (node instanceof nodes.ValNode)
            return this.cVal(node);
        else if (node instanceof nodes.ConNode)
            return this.cCon(node);
        else if (node instanceof nodes.BinNode)
            return this.cBin(node);
        else if (node instanceof nodes.VecNode)
            return this.cVec(node);
        throw new Error('UNKNOWN_NODE');
    }
    cObj(obj) {
        const map = {};
        obj.nodes((node, key) => {
            map[key] = this.cNode(node);
        });
        return {
            type: 'obj',
            id: this.cTs(obj.id),
            map,
        };
    }
    cVec(obj) {
        const map = [];
        const elements = obj.elements;
        const length = elements.length;
        const index = this.model.index;
        for (let i = 0; i < length; i++) {
            const element = elements[i];
            if (element === undefined)
                map.push(null);
            else
                map.push(this.cNode(index.get(element)));
        }
        return {
            type: 'vec',
            id: this.cTs(obj.id),
            map,
        };
    }
    cArr(obj) {
        const chunks = [];
        const iterator = obj.iterator();
        let chunk;
        while ((chunk = iterator()))
            chunks.push(this.cArrChunk(chunk));
        return {
            type: 'arr',
            id: this.cTs(obj.id),
            chunks,
        };
    }
    cArrChunk(chunk) {
        if (chunk.del) {
            const tombstone = {
                id: this.cTs(chunk.id),
                span: chunk.span,
            };
            return tombstone;
        }
        const index = this.model.index;
        const res = {
            id: this.cTs(chunk.id),
            value: chunk.data.map((n) => this.cNode(index.get(n))),
        };
        return res;
    }
    cStr(obj) {
        const chunks = [];
        const iterator = obj.iterator();
        let chunk;
        while ((chunk = iterator()))
            chunks.push(this.cStrChunk(chunk));
        return {
            type: 'str',
            id: this.cTs(obj.id),
            chunks,
        };
    }
    cStrChunk(chunk) {
        if (chunk.del) {
            const tombstone = {
                id: this.cTs(chunk.id),
                span: chunk.span,
            };
            return tombstone;
        }
        const res = {
            id: this.cTs(chunk.id),
            value: chunk.data,
        };
        return res;
    }
    cBin(obj) {
        const chunks = [];
        const iterator = obj.iterator();
        let chunk;
        while ((chunk = iterator()))
            chunks.push(this.cBinChunk(chunk));
        return {
            type: 'bin',
            id: this.cTs(obj.id),
            chunks,
        };
    }
    cBinChunk(chunk) {
        if (chunk.del) {
            const tombstone = {
                id: this.cTs(chunk.id),
                span: chunk.span,
            };
            return tombstone;
        }
        const res = {
            id: this.cTs(chunk.id),
            value: (0, toBase64_1.toBase64)(chunk.data),
        };
        return res;
    }
    cVal(obj) {
        return {
            type: 'val',
            id: this.cTs(obj.id),
            value: this.cNode(obj.node()),
        };
    }
    cCon(obj) {
        const node = {
            type: 'con',
            id: this.cTs(obj.id),
        };
        const val = obj.val;
        if (val instanceof clock_1.Timestamp) {
            node.timestamp = true;
            node.value = this.cTs(val);
        }
        else {
            if (val !== undefined)
                node.value = val;
        }
        return node;
    }
}
exports.Encoder = Encoder;
//# sourceMappingURL=Encoder.js.map