"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.sequenceFieldChangeCodecFactory = void 0;
const core_utils_1 = require("@fluidframework/core-utils");
const typebox_1 = require("@sinclair/typebox");
const util_1 = require("../../util");
const codec_1 = require("../../codec");
const types_1 = require("./types");
const format_1 = require("./format");
const utils_1 = require("./utils");
const sequenceFieldChangeCodecFactory = (childCodec) => (0, codec_1.makeCodecFamily)([[0, makeV0Codec(childCodec)]]);
exports.sequenceFieldChangeCodecFactory = sequenceFieldChangeCodecFactory;
function makeV0Codec(childCodec) {
    const markEffectCodec = {
        encode(effect) {
            const type = effect.type;
            switch (type) {
                case "MoveIn":
                    return {
                        moveIn: {
                            finalEndpoint: effect.finalEndpoint,
                            id: effect.id,
                        },
                    };
                case "Insert":
                    return { insert: { revision: effect.revision, id: effect.id } };
                case "Delete":
                    return {
                        delete: {
                            revision: effect.revision,
                            detachIdOverride: effect.detachIdOverride,
                            id: effect.id,
                        },
                    };
                case "MoveOut":
                    return {
                        moveOut: {
                            finalEndpoint: effect.finalEndpoint,
                            detachIdOverride: effect.detachIdOverride,
                            id: effect.id,
                        },
                    };
                case "AttachAndDetach":
                    return {
                        attachAndDetach: {
                            attach: markEffectCodec.encode(effect.attach),
                            detach: markEffectCodec.encode(effect.detach),
                        },
                    };
                case types_1.NoopMarkType:
                case "Placeholder":
                    (0, util_1.fail)(`Mark type: ${type} should not be encoded.`);
                default:
                    (0, core_utils_1.unreachableCase)(type);
            }
        },
        decode(encoded) {
            return decoderLibrary.dispatch(encoded);
        },
    };
    const decoderLibrary = new codec_1.DiscriminatedUnionDispatcher({
        moveIn(encoded) {
            const { id, finalEndpoint } = encoded;
            const mark = {
                type: "MoveIn",
                id,
            };
            if (finalEndpoint !== undefined) {
                mark.finalEndpoint = finalEndpoint;
            }
            return mark;
        },
        insert(encoded) {
            const { id, revision } = encoded;
            const mark = {
                type: "Insert",
                id,
            };
            if (revision !== undefined) {
                mark.revision = revision;
            }
            return mark;
        },
        delete(encoded) {
            const { id, revision, detachIdOverride } = encoded;
            const mark = {
                type: "Delete",
                id,
            };
            if (revision !== undefined) {
                mark.revision = revision;
            }
            if (detachIdOverride !== undefined) {
                mark.detachIdOverride = detachIdOverride;
            }
            return mark;
        },
        moveOut(encoded) {
            const { id, finalEndpoint, detachIdOverride } = encoded;
            const mark = {
                type: "MoveOut",
                id,
            };
            if (finalEndpoint !== undefined) {
                mark.finalEndpoint = finalEndpoint;
            }
            if (detachIdOverride !== undefined) {
                mark.detachIdOverride = detachIdOverride;
            }
            return mark;
        },
        attachAndDetach(encoded) {
            return {
                type: "AttachAndDetach",
                attach: decoderLibrary.dispatch(encoded.attach),
                detach: decoderLibrary.dispatch(encoded.detach),
            };
        },
    });
    const cellIdCodec = {
        encode: ({ localId, adjacentCells, lineage, revision }) => {
            const encoded = {
                localId,
                adjacentCells: adjacentCells?.map(({ id, count }) => [id, count]),
                // eslint-disable-next-line @typescript-eslint/no-shadow
                lineage: lineage?.map(({ revision, id, count, offset }) => [
                    revision,
                    id,
                    count,
                    offset,
                ]),
                revision,
            };
            return encoded;
        },
        decode: ({ localId, adjacentCells, lineage, revision }) => {
            // Note: this isn't inlined on decode so that round-tripping changes compare as deep-equal works,
            // which is mostly just a convenience for tests. On encode, JSON.stringify() takes care of removing
            // explicit undefined properties.
            const decoded = {
                localId,
            };
            if (revision !== undefined) {
                decoded.revision = revision;
            }
            if (adjacentCells !== undefined) {
                decoded.adjacentCells = adjacentCells.map(([id, count]) => ({
                    id,
                    count,
                }));
            }
            if (lineage !== undefined) {
                // eslint-disable-next-line @typescript-eslint/no-shadow
                decoded.lineage = lineage.map(([revision, id, count, offset]) => ({
                    revision,
                    id,
                    count,
                    offset,
                }));
            }
            return decoded;
        },
    };
    return {
        encode: (changeset) => {
            const jsonMarks = [];
            for (const mark of changeset) {
                const encodedMark = {
                    count: mark.count,
                };
                if (!(0, utils_1.isNoopMark)(mark)) {
                    encodedMark.effect = markEffectCodec.encode(mark);
                }
                if (mark.cellId !== undefined) {
                    encodedMark.cellId = cellIdCodec.encode(mark.cellId);
                }
                if (mark.changes !== undefined) {
                    encodedMark.changes = childCodec.encode(mark.changes);
                }
                jsonMarks.push(encodedMark);
            }
            return jsonMarks;
        },
        decode: (changeset) => {
            const marks = [];
            for (const mark of changeset) {
                const decodedMark = {
                    count: mark.count,
                };
                if (mark.effect !== undefined) {
                    Object.assign(decodedMark, markEffectCodec.decode(mark.effect));
                }
                if (mark.cellId !== undefined) {
                    decodedMark.cellId = cellIdCodec.decode(mark.cellId);
                }
                if (mark.changes !== undefined) {
                    decodedMark.changes = childCodec.decode(mark.changes);
                }
                marks.push(decodedMark);
            }
            return marks;
        },
        encodedSchema: (0, format_1.Changeset)(childCodec.encodedSchema ?? typebox_1.Type.Any()),
    };
}
//# sourceMappingURL=sequenceFieldCodecs.js.map