"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.sequenceFieldToDelta = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../../core/index.js");
const index_js_2 = require("../../util/index.js");
const deltaUtils_js_1 = require("../deltaUtils.js");
const moveEffectTable_js_1 = require("./moveEffectTable.js");
const types_js_1 = require("./types.js");
const utils_js_1 = require("./utils.js");
function sequenceFieldToDelta(change, deltaFromChild) {
    const local = [];
    const global = [];
    const rename = [];
    for (const mark of change) {
        const deltaMark = { count: mark.count };
        const inputCellId = (0, utils_js_1.getInputCellId)(mark);
        const changes = mark.changes;
        if (changes !== undefined) {
            const nestedDelta = deltaFromChild(changes);
            if (nestedDelta.size > 0) {
                if (inputCellId === undefined) {
                    deltaMark.fields = nestedDelta;
                }
                else {
                    global.push({
                        id: (0, deltaUtils_js_1.nodeIdFromChangeAtom)(inputCellId),
                        fields: nestedDelta,
                    });
                }
            }
        }
        if (!(0, utils_js_1.areInputCellsEmpty)(mark) && !(0, utils_js_1.areOutputCellsEmpty)(mark)) {
            // Since each cell is associated with exactly one node,
            // the cell starting end ending populated means the cell content has not changed.
            local.push(deltaMark);
        }
        else if ((0, utils_js_1.isAttachAndDetachEffect)(mark)) {
            (0, internal_1.assert)(inputCellId !== undefined, 0x81e /* AttachAndDetach mark should have defined input cell ID */);
            // The cell starting and ending empty means the cell content has not changed,
            // unless transient content was inserted/attached.
            if ((0, moveEffectTable_js_1.isMoveIn)(mark.attach) && (0, moveEffectTable_js_1.isMoveOut)(mark.detach)) {
                (0, internal_1.assert)(mark.changes === undefined, 0x81f /* AttachAndDetach moves should not have changes */);
                continue;
            }
            const outputId = (0, utils_js_1.getDetachedNodeId)(mark.detach);
            (0, internal_1.assert)(outputId !== undefined, 0x820 /* AttachAndDetach mark should have defined output cell ID */);
            const oldId = (0, deltaUtils_js_1.nodeIdFromChangeAtom)((0, moveEffectTable_js_1.isMoveIn)(mark.attach) ? (0, utils_js_1.getEndpoint)(mark.attach) : inputCellId);
            if (!(0, index_js_1.areEqualChangeAtomIds)(inputCellId, outputId)) {
                rename.push({
                    count: mark.count,
                    oldId,
                    newId: (0, deltaUtils_js_1.nodeIdFromChangeAtom)(outputId),
                });
            }
            if (deltaMark.fields) {
                global.push({
                    id: oldId,
                    fields: deltaMark.fields,
                });
            }
        }
        else {
            const type = mark.type;
            // Inline into `switch(mark.type)` once we upgrade to TS 4.7
            switch (type) {
                case "MoveIn": {
                    local.push({
                        attach: (0, deltaUtils_js_1.nodeIdFromChangeAtom)((0, utils_js_1.getEndpoint)(mark)),
                        count: mark.count,
                    });
                    break;
                }
                case "Remove": {
                    const newDetachId = (0, utils_js_1.getDetachedNodeId)(mark);
                    if (inputCellId === undefined) {
                        deltaMark.detach = (0, deltaUtils_js_1.nodeIdFromChangeAtom)(newDetachId);
                        local.push(deltaMark);
                    }
                    else {
                        const oldId = (0, deltaUtils_js_1.nodeIdFromChangeAtom)(inputCellId);
                        // Removal of already removed content is only a no-op if the detach IDs are different.
                        if (!(0, index_js_1.areEqualChangeAtomIds)(inputCellId, newDetachId)) {
                            rename.push({
                                count: mark.count,
                                oldId,
                                newId: (0, deltaUtils_js_1.nodeIdFromChangeAtom)(newDetachId),
                            });
                        }
                        // In all cases, the nested changes apply
                        if (deltaMark.fields) {
                            global.push({
                                id: oldId,
                                fields: deltaMark.fields,
                            });
                        }
                    }
                    break;
                }
                case "MoveOut": {
                    // The move destination will look for the detach ID of the source, so we can ignore `finalEndpoint`.
                    const detachId = (0, deltaUtils_js_1.nodeIdFromChangeAtom)((0, utils_js_1.getDetachedNodeId)(mark));
                    if (inputCellId === undefined) {
                        deltaMark.detach = detachId;
                        local.push(deltaMark);
                    }
                    else {
                        // Move sources implicitly restore their content
                        rename.push({
                            count: mark.count,
                            oldId: (0, deltaUtils_js_1.nodeIdFromChangeAtom)(inputCellId),
                            newId: detachId,
                        });
                    }
                    break;
                }
                case "Insert": {
                    (0, internal_1.assert)(inputCellId !== undefined, 0x821 /* Active Insert marks must have a CellId */);
                    const buildId = (0, deltaUtils_js_1.nodeIdFromChangeAtom)(inputCellId);
                    deltaMark.attach = buildId;
                    if (deltaMark.fields) {
                        // Nested changes are represented on the node in its starting location
                        global.push({ id: buildId, fields: deltaMark.fields });
                        delete deltaMark.fields;
                    }
                    local.push(deltaMark);
                    break;
                }
                case types_js_1.NoopMarkType:
                    if (inputCellId === undefined) {
                        local.push(deltaMark);
                    }
                    break;
                case "Rename":
                    (0, internal_1.assert)(mark.cellId !== undefined, 0x9f9 /* Renames should only target empty cells */);
                    break;
                default:
                    (0, internal_1.unreachableCase)(type);
            }
        }
    }
    // Remove trailing no-op marks
    while ((0, index_js_2.hasSome)(local)) {
        const lastMark = (0, index_js_2.getLast)(local);
        if (lastMark.attach !== undefined ||
            lastMark.detach !== undefined ||
            lastMark.fields !== undefined) {
            break;
        }
        local.pop();
    }
    const delta = {};
    if (local.length > 0) {
        delta.local = local;
    }
    if (global.length > 0) {
        delta.global = global;
    }
    if (rename.length > 0) {
        delta.rename = rename;
    }
    return delta;
}
exports.sequenceFieldToDelta = sequenceFieldToDelta;
//# sourceMappingURL=sequenceFieldToDelta.js.map