"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeOptionalFieldCodec = void 0;
const index_js_1 = require("../../core/index.js");
const changeAtomIdCodec_js_1 = require("../changeAtomIdCodec.js");
const index_js_2 = require("../modular-schema/index.js");
const optionalFieldChangeFormatV2_js_1 = require("./optionalFieldChangeFormatV2.js");
const internal_1 = require("@fluidframework/core-utils/internal");
function makeRegisterIdCodec(changeAtomIdCodec) {
    return {
        encode: (registerId, context) => {
            if (registerId === "self") {
                return null;
            }
            return changeAtomIdCodec.encode(registerId, context);
        },
        decode: (registerId, context) => {
            if (registerId === null) {
                return "self";
            }
            return changeAtomIdCodec.decode(registerId, context);
        },
        encodedSchema: optionalFieldChangeFormatV2_js_1.EncodedRegisterId,
    };
}
function makeOptionalFieldCodec(revisionTagCodec) {
    const changeAtomIdCodec = (0, changeAtomIdCodec_js_1.makeChangeAtomIdCodec)(revisionTagCodec);
    const registerIdCodec = makeRegisterIdCodec(changeAtomIdCodec);
    return {
        encode: (change, context) => {
            (0, internal_1.assert)(change.nodeDetach === undefined ||
                (0, index_js_1.areEqualChangeAtomIdOpts)(change.valueReplace?.src, change.nodeDetach), "This format only supports node detach when it represents a pin");
            const encoded = {};
            if (change.valueReplace !== undefined) {
                encoded.r = {
                    e: change.valueReplace.isEmpty,
                    d: changeAtomIdCodec.encode(change.valueReplace.dst, context.baseContext),
                };
                if (change.valueReplace.src !== undefined) {
                    const srcRegister = change.nodeDetach === undefined ? change.valueReplace.src : "self";
                    encoded.r.s = registerIdCodec.encode(srcRegister, context.baseContext);
                }
            }
            const encodedChildChanges = [];
            if (change.childChange !== undefined) {
                encodedChildChanges.push([null, context.encodeNode(change.childChange)]);
            }
            for (const [detachId, nodeId] of context.rootNodeChanges.entries()) {
                encodedChildChanges.push([
                    changeAtomIdCodec.encode({ revision: detachId[0], localId: detachId[1] }, context.baseContext),
                    context.encodeNode(nodeId),
                ]);
            }
            if (encodedChildChanges.length > 0) {
                encoded.c = encodedChildChanges;
            }
            const encodedMoves = [];
            for (const { start: oldId, value: newId, length: count, } of context.rootRenames.entries()) {
                (0, internal_1.assert)(count === 1, "Unexpected range rename in optional field");
                encodedMoves.push([
                    changeAtomIdCodec.encode(oldId, context.baseContext),
                    changeAtomIdCodec.encode(newId, context.baseContext),
                ]);
            }
            if (encodedMoves.length > 0) {
                encoded.m = encodedMoves;
            }
            return encoded;
        },
        decode: (encoded, context) => {
            const decoded = {};
            if (encoded.r !== undefined) {
                const replace = {
                    isEmpty: encoded.r.e,
                    dst: changeAtomIdCodec.decode(encoded.r.d, context.baseContext),
                };
                if (encoded.r.s !== undefined) {
                    const register = registerIdCodec.decode(encoded.r.s, context.baseContext);
                    if (register === "self") {
                        // Note that this is safe as long as we assume that this change will not be rebased
                        // over a move to a sequence field.
                        // The ID of an attach and accompanying detach/rename is arbitrary, except in sequence field where
                        // the ID of the detach becomes a cell ID which may be referenced by other changes.
                        replace.src = context.generateId();
                        decoded.nodeDetach = replace.src;
                    }
                    else {
                        replace.src = register;
                    }
                }
                decoded.valueReplace = replace;
            }
            if (encoded.c !== undefined) {
                for (const [encodedDetachId, nodeChange] of encoded.c) {
                    if (encodedDetachId !== null) {
                        context.decodeRootNodeChange(changeAtomIdCodec.decode(encodedDetachId, context.baseContext), nodeChange);
                    }
                    else {
                        decoded.childChange = context.decodeNode(nodeChange);
                    }
                }
            }
            for (const [encodedOldId, encodedNewId] of encoded.m ?? []) {
                context.decodeRootRename(changeAtomIdCodec.decode(encodedOldId, context.baseContext), changeAtomIdCodec.decode(encodedNewId, context.baseContext), 1);
            }
            return decoded;
        },
        encodedSchema: (0, optionalFieldChangeFormatV2_js_1.EncodedOptionalChangeset)(index_js_2.EncodedNodeChangeset),
    };
}
exports.makeOptionalFieldCodec = makeOptionalFieldCodec;
//# sourceMappingURL=optionalFieldCodecV2.js.map