"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 core_utils_1 = require("@fluidframework/core-utils");
const util_1 = require("../../util");
const core_1 = require("../../core");
const deltaUtils_1 = require("../deltaUtils");
const treeTextCursor_1 = require("../treeTextCursor");
const format_1 = require("./format");
const utils_1 = require("./utils");
function sequenceFieldToDelta({ change, revision }, deltaFromChild) {
    const local = [];
    const global = [];
    const build = [];
    const rename = [];
    for (const mark of change) {
        const deltaMark = { count: mark.count };
        const changes = (0, utils_1.getEffectiveNodeChanges)(mark);
        if (changes !== undefined) {
            deltaMark.fields = deltaFromChild(changes);
            if (deltaMark.fields.size === 0) {
                delete deltaMark.fields;
            }
        }
        if (!(0, utils_1.areInputCellsEmpty)(mark) && !(0, utils_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_1.areInputCellsEmpty)(mark) && (0, utils_1.areOutputCellsEmpty)(mark)) {
            // The cell starting and ending empty means the cell content has not changed,
            // unless transient content was inserted/attached.
            if ((0, utils_1.markIsTransient)(mark)) {
                const oldId = (0, deltaUtils_1.nodeIdFromChangeAtom)(mark.cellId);
                if (!(0, core_1.areEqualChangeAtomIds)(mark.cellId, mark.transientDetach)) {
                    // TODO: handle transient move-in/return-to
                    (0, core_utils_1.assert)((0, utils_1.isInsert)(mark), 0x7d9 /* Unexpected non-insert transient mark */);
                    if (mark.content !== undefined) {
                        build.push({
                            id: oldId,
                            trees: mark.content.map(treeTextCursor_1.singleTextCursor),
                        });
                    }
                    rename.push({
                        count: mark.count,
                        oldId,
                        newId: (0, deltaUtils_1.nodeIdFromChangeAtom)(mark.transientDetach),
                    });
                }
                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, core_1.makeDetachedNodeId)(mark.revision ?? revision, mark.id),
                        count: mark.count,
                    });
                    break;
                }
                case "Delete": {
                    if (mark.cellId === undefined) {
                        deltaMark.detach = (0, core_1.makeDetachedNodeId)(mark.revision ?? revision, mark.id);
                        local.push(deltaMark);
                    }
                    else {
                        // Removal of already removed content is a no-op.
                        // It does not relocate the content to the detached field that would otherwise be created.
                        // TODO: pass on nested changes if any
                    }
                    break;
                }
                case "MoveOut":
                case "ReturnFrom": {
                    const detachId = (0, core_1.makeDetachedNodeId)(mark.revision ?? revision, mark.id);
                    if (mark.cellId === undefined) {
                        deltaMark.detach = detachId;
                        local.push(deltaMark);
                    }
                    else {
                        // TODO: relocate content to `detachId` field
                    }
                    break;
                }
                case "Insert": {
                    (0, core_utils_1.assert)(mark.transientDetach === undefined, 0x7da /* Unexpected transient insert */);
                    (0, core_utils_1.assert)(mark.cellId !== undefined, 0x7db /* Active insert mark must have CellId */);
                    const buildId = (0, deltaUtils_1.nodeIdFromChangeAtom)(mark.cellId);
                    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;
                    }
                    if ((0, utils_1.isNewAttach)(mark)) {
                        (0, core_utils_1.assert)(mark.content !== undefined, 0x7dc /* New insert must have content */);
                        build.push({
                            id: buildId,
                            trees: mark.content.map(treeTextCursor_1.singleTextCursor),
                        });
                    }
                    local.push(deltaMark);
                    break;
                }
                case format_1.NoopMarkType:
                    (0, util_1.fail)("Unexpected NoopMarkType where cell is supposed to be affected");
                case "Placeholder":
                    (0, util_1.fail)("Should not have placeholders in a changeset being converted to delta");
                default:
                    (0, core_utils_1.unreachableCase)(type);
            }
        }
    }
    // Remove trailing no-op marks
    while (local.length > 0) {
        const lastMark = local[local.length - 1];
        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 (build.length > 0) {
        delta.build = build;
    }
    if (rename.length > 0) {
        delta.rename = rename;
    }
    return delta;
}
exports.sequenceFieldToDelta = sequenceFieldToDelta;
//# sourceMappingURL=sequenceFieldToDelta.js.map