"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 ClockTable_1 = require("../../../../json-crdt-patch/codec/clock/ClockTable");
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("../../structural/binary/constants");
class Decoder {
    constructor(reader) {
        this.decodeStrChunk = () => {
            const decoder = this.dec;
            const _reader = decoder.reader;
            const id = this.ts();
            const val = decoder.val();
            if (typeof val === 'string')
                return new nodes.StrChunk(id, val.length, val);
            return new nodes.StrChunk(id, ~~val, '');
        };
        this.decodeBinChunk = () => {
            const id = this.ts();
            const reader = this.dec.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.decodeArrChunk = () => {
            const id = this.ts();
            const reader = this.dec.reader;
            const [deleted, length] = reader.b1vu56();
            if (deleted)
                return new nodes.ArrChunk(id, length, undefined);
            else {
                const data = [];
                for (let i = 0; i < length; i++)
                    data.push(this.ts());
                return new nodes.ArrChunk(id, length, data);
            }
        };
        this.dec = new CborDecoderBase_1.CborDecoderBase(reader || new CrdtReader_1.CrdtReader());
    }
    decode(fields, ModelConstructor = Model_1.Model) {
        const reader = this.dec.reader;
        reader.reset(fields.c);
        const clockTable = (this.clockTable = ClockTable_1.ClockTable.decode(reader));
        return this.decodeFields(clockTable, fields, ModelConstructor);
    }
    decodeFields(clockTable, fields, ModelConstructor = Model_1.Model) {
        const reader = this.dec.reader;
        const firstClock = clockTable.byIdx[0];
        const vectorClock = new clock_1.ClockVector(firstClock.sid, firstClock.time + 1);
        const doc = (this.doc = new ModelConstructor(vectorClock));
        const root = fields.r;
        if (root && root.length) {
            reader.reset(root);
            const rootValue = this.ts();
            doc.root.set(rootValue);
        }
        const index = doc.index;
        const keys = Object.keys(fields);
        const length = keys.length;
        for (let i = 0; i < length; i++) {
            const field = keys[i];
            if (field.length < 3)
                continue; // Skip "c" and "r".
            const arr = fields[field];
            const id = clockTable.parseField(field);
            reader.reset(arr);
            const node = this.decodeNode(id);
            index.set(id, node);
        }
        return doc;
    }
    ts() {
        const [sessionIndex, timeDiff] = this.dec.reader.id();
        return new clock_1.Timestamp(this.clockTable.byIdx[sessionIndex].sid, timeDiff);
    }
    decodeNode(id) {
        const reader = this.dec.reader;
        const octet = reader.u8();
        const major = octet >> 5;
        const minor = octet & 0b11111;
        const length = minor < 24 ? minor : minor === 24 ? reader.u8() : minor === 25 ? reader.u16() : reader.u32();
        switch (major) {
            case constants_1.CRDT_MAJOR.CON:
                return this.decodeCon(id, length);
            case constants_1.CRDT_MAJOR.VAL:
                return this.decodeVal(id);
            case constants_1.CRDT_MAJOR.OBJ:
                return this.decodeObj(id, length);
            case constants_1.CRDT_MAJOR.VEC:
                return this.decodeVec(id, length);
            case constants_1.CRDT_MAJOR.STR:
                return this.decodeStr(id, length);
            case constants_1.CRDT_MAJOR.BIN:
                return this.decodeBin(id, length);
            case constants_1.CRDT_MAJOR.ARR:
                return this.decodeArr(id, length);
        }
        return Model_1.UNDEFINED;
    }
    decodeCon(id, length) {
        const decoder = this.dec;
        const data = !length ? decoder.val() : this.ts();
        const node = new nodes.ConNode(id, data);
        return node;
    }
    decodeVal(id) {
        const val = this.ts();
        const node = new nodes.ValNode(this.doc, id, val);
        return node;
    }
    decodeObj(id, length) {
        const decoder = this.dec;
        const obj = new nodes.ObjNode(this.doc, id);
        const keys = obj.keys;
        for (let i = 0; i < length; i++) {
            const key = decoder.val() + '';
            const val = this.ts();
            keys.set(key, val);
        }
        return obj;
    }
    decodeVec(id, length) {
        const reader = this.dec.reader;
        const node = new nodes.VecNode(this.doc, id);
        const elements = node.elements;
        for (let i = 0; i < length; i++) {
            const octet = reader.u8();
            if (!octet)
                elements.push(undefined);
            else
                elements.push(this.ts());
        }
        return node;
    }
    decodeStr(id, length) {
        const node = new nodes.StrNode(id);
        node.ingest(length, this.decodeStrChunk);
        return node;
    }
    decodeBin(id, length) {
        const node = new nodes.BinNode(id);
        node.ingest(length, this.decodeBinChunk);
        return node;
    }
    decodeArr(id, length) {
        const node = new nodes.ArrNode(this.doc, id);
        node.ingest(length, this.decodeArrChunk);
        return node;
    }
}
exports.Decoder = Decoder;
//# sourceMappingURL=Decoder.js.map