import { CrdtReader } from '../../util/binary/CrdtReader';
import { interval, ClockVector, ServerClockVector, Timestamp, } from '../../clock';
import { PatchBuilder } from '../../PatchBuilder';
import { CborDecoder } from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder';
/**
 * JSON CRDT Patch "binary" codec decoder.
 */
export class Decoder extends CborDecoder {
    builder;
    patchSid;
    /**
     * Creates a new JSON CRDT patch decoder.
     *
     * @param reader An optional custom implementation of a CRDT decoder.
     */
    constructor(reader = new CrdtReader()) {
        super(reader);
    }
    /**
     * Decodes a JSON CRDT patch from a binary blob.
     *
     * @param data Binary data to decode.
     * @returns A JSON CRDT patch.
     */
    decode(data) {
        this.reader.reset(data);
        return this.readPatch();
    }
    readPatch() {
        const reader = this.reader;
        const sid = reader.vu57();
        const time = reader.vu57();
        const isServerClock = sid === 1 /* SESSION.SERVER */;
        const clock = isServerClock ? new ServerClockVector(1 /* SESSION.SERVER */, time) : new ClockVector(sid, time);
        this.patchSid = clock.sid;
        const builder = (this.builder = new PatchBuilder(clock));
        const map = this.val();
        if (Array.isArray(map))
            builder.patch.meta = map[0];
        this.decodeOperations();
        return builder.patch;
    }
    decodeId() {
        const reader = this.reader;
        const [isSessionDifferent, x] = reader.b1vu56();
        return isSessionDifferent ? new Timestamp(reader.vu57(), x) : new Timestamp(this.patchSid, x);
    }
    decodeTss() {
        const id = this.decodeId();
        const span = this.reader.vu57();
        return interval(id, 0, span);
    }
    decodeOperations() {
        const reader = this.reader;
        const length = reader.vu57();
        for (let i = 0; i < length; i++)
            this.decodeOperation();
    }
    decodeOperation() {
        const builder = this.builder;
        const reader = this.reader;
        const octet = reader.u8();
        const opcode = octet >> 3;
        switch (opcode) {
            case 0 /* JsonCrdtPatchOpcode.new_con */: {
                const length = octet & 0b111;
                builder.con(!length ? this.val() : this.decodeId());
                break;
            }
            case 1 /* JsonCrdtPatchOpcode.new_val */: {
                builder.val();
                break;
            }
            case 2 /* JsonCrdtPatchOpcode.new_obj */: {
                builder.obj();
                break;
            }
            case 3 /* JsonCrdtPatchOpcode.new_vec */: {
                builder.vec();
                break;
            }
            case 4 /* JsonCrdtPatchOpcode.new_str */: {
                builder.str();
                break;
            }
            case 5 /* JsonCrdtPatchOpcode.new_bin */: {
                builder.bin();
                break;
            }
            case 6 /* JsonCrdtPatchOpcode.new_arr */: {
                builder.arr();
                break;
            }
            case 9 /* JsonCrdtPatchOpcode.ins_val */: {
                const obj = this.decodeId();
                const val = this.decodeId();
                builder.setVal(obj, val);
                break;
            }
            case 10 /* JsonCrdtPatchOpcode.ins_obj */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const tuples = [];
                for (let i = 0; i < length; i++) {
                    const key = this.val();
                    if (typeof key !== 'string')
                        continue;
                    const value = this.decodeId();
                    tuples.push([key, value]);
                }
                builder.insObj(obj, tuples);
                break;
            }
            case 11 /* JsonCrdtPatchOpcode.ins_vec */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const tuples = [];
                for (let i = 0; i < length; i++) {
                    const index = this.val();
                    if (typeof index !== 'number')
                        continue;
                    const value = this.decodeId();
                    tuples.push([index, value]);
                }
                builder.insVec(obj, tuples);
                break;
            }
            case 12 /* JsonCrdtPatchOpcode.ins_str */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const after = this.decodeId();
                const str = reader.utf8(length);
                builder.insStr(obj, after, str);
                break;
            }
            case 13 /* JsonCrdtPatchOpcode.ins_bin */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const after = this.decodeId();
                const buf = reader.buf(length);
                if (!(buf instanceof Uint8Array))
                    return;
                builder.insBin(obj, after, buf);
                break;
            }
            case 14 /* JsonCrdtPatchOpcode.ins_arr */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const after = this.decodeId();
                const elements = [];
                for (let i = 0; i < length; i++)
                    elements.push(this.decodeId());
                builder.insArr(obj, after, elements);
                break;
            }
            case 15 /* JsonCrdtPatchOpcode.upd_arr */: {
                const obj = this.decodeId();
                const ref = this.decodeId();
                const val = this.decodeId();
                builder.updArr(obj, ref, val);
                break;
            }
            case 16 /* JsonCrdtPatchOpcode.del */: {
                const length = octet & 0b111 || reader.vu57();
                const obj = this.decodeId();
                const what = [];
                for (let i = 0; i < length; i++)
                    what.push(this.decodeTss());
                builder.del(obj, what);
                break;
            }
            case 17 /* JsonCrdtPatchOpcode.nop */: {
                const length = octet & 0b111 || reader.vu57();
                builder.nop(length);
                break;
            }
            default: {
                throw new Error('UNKNOWN_OP');
            }
        }
    }
}
//# sourceMappingURL=Decoder.js.map