/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert, unreachableCase } from "@fluidframework/common-utils";
import { brandOpaque, fail, OffsetListFactory } from "../../util";
import { Delta } from "../../core";
import { singleTextCursor } from "../treeTextCursor";
import { NoopMarkType } from "./format";
import { areInputCellsEmpty, areOutputCellsEmpty, getEffectiveNodeChanges, markIsTransient, } from "./utils";
export function sequenceFieldToDelta(marks, deltaFromChild) {
    const out = new OffsetListFactory();
    for (const mark of marks) {
        const changes = getEffectiveNodeChanges(mark);
        const cellDelta = cellDeltaFromMark(mark, changes === undefined);
        const fullDelta = withChildModificationsIfAny(changes, cellDelta, deltaFromChild);
        out.push(fullDelta);
    }
    return out.list;
}
function cellDeltaFromMark(mark, ignoreTransient) {
    if (!areInputCellsEmpty(mark) && !areOutputCellsEmpty(mark)) {
        // Since each cell is associated with exactly one node,
        // the cell starting end ending populated means the cell content has not changed.
        return mark.count;
    }
    else if (areInputCellsEmpty(mark) &&
        areOutputCellsEmpty(mark) &&
        (!markIsTransient(mark) || ignoreTransient)) {
        // The cell starting and ending empty means the cell content has not changed,
        // unless transient content was inserted/attached.
        return 0;
    }
    else {
        const type = mark.type;
        // Inline into `switch(mark.type)` once we upgrade to TS 4.7
        switch (type) {
            case "Insert": {
                const cursors = mark.content.map(singleTextCursor);
                const insertMark = {
                    type: Delta.MarkType.Insert,
                    content: cursors,
                };
                if (mark.transientDetach !== undefined) {
                    insertMark.isTransient = true;
                }
                return insertMark;
            }
            case "MoveIn":
            case "ReturnTo": {
                return {
                    type: Delta.MarkType.MoveIn,
                    count: mark.count,
                    moveId: brandOpaque(mark.id),
                };
            }
            case NoopMarkType: {
                return mark.count;
            }
            case "Delete": {
                return {
                    type: Delta.MarkType.Delete,
                    count: mark.count,
                };
            }
            case "MoveOut":
            case "ReturnFrom": {
                return {
                    type: Delta.MarkType.MoveOut,
                    moveId: brandOpaque(mark.id),
                    count: mark.count,
                };
            }
            case "Revive": {
                const insertMark = {
                    type: Delta.MarkType.Insert,
                    content: mark.content,
                };
                if (mark.transientDetach !== undefined) {
                    insertMark.isTransient = true;
                }
                return insertMark;
            }
            case "Placeholder":
                fail("Should not have placeholders in a changeset being converted to delta");
            default:
                unreachableCase(type);
        }
    }
}
function withChildModificationsIfAny(changes, deltaMark, deltaFromChild) {
    if (changes !== undefined) {
        const modify = deltaFromChild(changes);
        if (modify.fields !== undefined) {
            if (typeof deltaMark === "number") {
                assert(deltaMark === 1, 0x72d /* Invalid nested changes on non-1 skip mark */);
                return modify;
            }
            else {
                assert(deltaMark.type !== Delta.MarkType.MoveIn, 0x72e /* Invalid nested changes on MoveIn mark */);
                return { ...deltaMark, fields: modify.fields };
            }
        }
    }
    return deltaMark;
}
//# sourceMappingURL=sequenceFieldToDelta.js.map