"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 ClockDecoder_1 = require("../../../../json-crdt-patch/codec/clock/ClockDecoder");
const CrdtReader_1 = require("../../../../json-crdt-patch/util/binary/CrdtReader");
const clock_1 = require("../../../../json-crdt-patch/clock");
const Model_1 = require("../../../model/Model");
const CborDecoderBase_1 = require("@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase");
const constants_1 = require("./constants");
class Decoder extends CborDecoderBase_1.CborDecoderBase {
    constructor() {
        super(new CrdtReader_1.CrdtReader());
        this.clockDecoder = undefined;
        this.time = -1;
        this.cStrChunk = () => {
            const id = this.ts();
            const val = this.val();
            if (typeof val === 'string')
                return new nodes.StrChunk(id, val.length, val);
            return new nodes.StrChunk(id, ~~val, '');
        };
        this.cBinChunk = () => {
            const id = this.ts();
            const reader = this.reader;
            const [deleted, length] = reader.b1vu56();
            if (deleted)
                return new nodes.BinChunk(id, length, undefined);
            return new nodes.BinChunk(id, length, reader.buf(length));
        };
        this.cArrChunk = () => {
            const id = this.ts();
            const [deleted, length] = this.reader.b1vu56();
            if (deleted)
                return new nodes.ArrChunk(id, length, undefined);
            const ids = [];
            for (let i = 0; i < length; i++)
                ids.push(this.cNode().id);
            return new nodes.ArrChunk(id, length, ids);
        };
    }
    decode(data, model) {
        this.clockDecoder = undefined;
        this.time = -1;
        const reader = this.reader;
        reader.reset(data);
        const isServerTime = reader.peak() & 0b10000000;
        if (isServerTime) {
            reader.x++;
            const time = (this.time = reader.vu57());
            if (!model)
                model = Model_1.Model.withServerClock(void 0, time);
        }
        else {
            this.decodeClockTable();
            if (!model) {
                const clock = this.clockDecoder.clock;
                model = Model_1.Model.create(void 0, clock);
            }
        }
        this.doc = model;
        model.root = new nodes.RootNode(this.doc, this.cRoot().id);
        this.clockDecoder = undefined;
        return model;
    }
    decodeClockTable() {
        const reader = this.reader;
        const clockTableOffset = reader.u32();
        const offset = reader.x;
        reader.x += clockTableOffset;
        const length = reader.vu57();
        const sessionId = reader.vu57();
        const time = reader.vu57();
        this.clockDecoder = new ClockDecoder_1.ClockDecoder(sessionId, time);
        for (let i = 1; i < length; i++) {
            const sid = reader.vu57();
            const time = reader.vu57();
            this.clockDecoder.pushTuple(sid, time);
        }
        reader.x = offset;
    }
    ts() {
        const decoderTime = this.time;
        const isLogical = decoderTime < 0;
        if (isLogical) {
            const [sessionIndex, timeDiff] = this.reader.id();
            return this.clockDecoder.decodeId(sessionIndex, timeDiff);
        }
        else {
            return new clock_1.Timestamp(1 /* SESSION.SERVER */, this.reader.vu57());
        }
    }
    cRoot() {
        const reader = this.reader;
        const peek = reader.uint8[reader.x];
        return !peek ? Model_1.UNDEFINED : this.cNode();
    }
    cNode() {
        const reader = this.reader;
        const id = this.ts();
        const octet = reader.u8();
        const major = octet >> 5;
        const minor = octet & 0b11111;
        switch (major) {
            case constants_1.CRDT_MAJOR.CON:
                return this.cCon(id, minor);
            case constants_1.CRDT_MAJOR.VAL:
                return this.cVal(id);
            case constants_1.CRDT_MAJOR.OBJ:
                return this.cObj(id, minor !== 0b11111 ? minor : reader.vu57());
            case constants_1.CRDT_MAJOR.VEC:
                return this.cVec(id, minor !== 0b11111 ? minor : reader.vu57());
            case constants_1.CRDT_MAJOR.STR:
                return this.cStr(id, minor !== 0b11111 ? minor : reader.vu57());
            case constants_1.CRDT_MAJOR.BIN:
                return this.cBin(id, minor !== 0b11111 ? minor : reader.vu57());
            case constants_1.CRDT_MAJOR.ARR:
                return this.cArr(id, minor !== 0b11111 ? minor : reader.vu57());
        }
        throw new Error('UNKNOWN_NODE');
    }
    cCon(id, length) {
        const doc = this.doc;
        const data = !length ? this.val() : this.ts();
        const node = new nodes.ConNode(id, data);
        doc.index.set(id, node);
        return node;
    }
    cVal(id) {
        const child = this.cNode();
        const doc = this.doc;
        const node = new nodes.ValNode(doc, id, child.id);
        doc.index.set(id, node);
        return node;
    }
    cObj(id, length) {
        const obj = new nodes.ObjNode(this.doc, id);
        for (let i = 0; i < length; i++)
            this.cObjChunk(obj);
        this.doc.index.set(id, obj);
        return obj;
    }
    cObjChunk(obj) {
        const key = this.key();
        obj.keys.set(key, this.cNode().id);
    }
    cVec(id, length) {
        const reader = this.reader;
        const obj = new nodes.VecNode(this.doc, id);
        const elements = obj.elements;
        for (let i = 0; i < length; i++) {
            const octet = reader.peak();
            if (!octet) {
                reader.x++;
                elements.push(undefined);
            }
            else
                elements.push(this.cNode().id);
        }
        this.doc.index.set(id, obj);
        return obj;
    }
    cStr(id, length) {
        const node = new nodes.StrNode(id);
        if (length)
            node.ingest(length, this.cStrChunk);
        this.doc.index.set(id, node);
        return node;
    }
    cBin(id, length) {
        const node = new nodes.BinNode(id);
        if (length)
            node.ingest(length, this.cBinChunk);
        this.doc.index.set(id, node);
        return node;
    }
    cArr(id, length) {
        const obj = new nodes.ArrNode(this.doc, id);
        if (length)
            obj.ingest(length, this.cArrChunk);
        this.doc.index.set(id, obj);
        return obj;
    }
}
exports.Decoder = Decoder;
//# sourceMappingURL=Decoder.js.map