"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertGenericChange = exports.genericFieldKind = exports.genericChangeHandler = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const sorted_btree_es6_1 = require("@tylerbu/sorted-btree-es6");
const index_js_1 = require("../../core/index.js");
const fieldKindWithEditor_js_1 = require("./fieldKindWithEditor.js");
const genericFieldKindCodecs_js_1 = require("./genericFieldKindCodecs.js");
const genericFieldKindTypes_js_1 = require("./genericFieldKindTypes.js");
/**
 * {@link FieldChangeHandler} implementation for {@link GenericChangeset}.
 */
exports.genericChangeHandler = {
    rebaser: {
        compose,
        invert: (change) => change,
        rebase: rebaseGenericChange,
        prune: pruneGenericChange,
        replaceRevisions,
        mute: (change) => change,
    },
    codecsFactory: genericFieldKindCodecs_js_1.makeGenericChangeCodec,
    editor: {
        buildChildChanges(changes) {
            return (0, genericFieldKindTypes_js_1.newGenericChangeset)(Array.from(changes));
        },
    },
    intoDelta: (change, deltaFromChild) => {
        let nodeIndex = 0;
        const markList = [];
        for (const [index, nodeChange] of change.entries()) {
            if (nodeIndex < index) {
                const offset = index - nodeIndex;
                markList.push({ count: offset });
                nodeIndex = index;
            }
            markList.push({ count: 1, fields: deltaFromChild(nodeChange) });
            nodeIndex += 1;
        }
        return { local: markList };
    },
    relevantRemovedRoots,
    isEmpty: (change) => change.length === 0,
    getNestedChanges,
    createEmpty: genericFieldKindTypes_js_1.newGenericChangeset,
    getCrossFieldKeys: (_change) => [],
};
function compose(change1, change2, composeChildren) {
    const composed = change1.clone();
    for (const [index, id2] of change2.entries()) {
        const id1 = composed.get(index);
        const idComposed = id1 !== undefined ? composeChildren(id1, id2) : id2;
        composed.set(index, idComposed);
    }
    return composed;
}
function getNestedChanges(change) {
    // For generic changeset, the indices in the input and output contexts are the same.
    return change.toArray().map(([index, nodeChange]) => [nodeChange, index, index]);
}
function rebaseGenericChange(change, over, rebaseChild) {
    const rebased = new sorted_btree_es6_1.BTree();
    let nextIndex = 0;
    // eslint-disable-next-line no-constant-condition
    while (true) {
        const newEntry = change.getPairOrNextHigher(nextIndex);
        const baseEntry = over.getPairOrNextHigher(nextIndex);
        if (baseEntry === undefined && newEntry === undefined) {
            break;
        }
        const newIndex = newEntry?.[0] ?? Number.POSITIVE_INFINITY;
        const baseIndex = baseEntry?.[0] ?? Number.POSITIVE_INFINITY;
        let newNodeChange;
        let baseNodeChange;
        let index;
        if (newIndex === baseIndex) {
            (0, internal_1.assert)(newEntry !== undefined && baseEntry !== undefined, 0xa0d /* Entries should be defined */);
            index = newIndex;
            newNodeChange = newEntry[1];
            baseNodeChange = baseEntry[1];
        }
        else if (newIndex < baseIndex) {
            (0, internal_1.assert)(newEntry !== undefined, 0xa0e /* Entry should be defined */);
            index = newIndex;
            newNodeChange = newEntry[1];
        }
        else {
            (0, internal_1.assert)(baseEntry !== undefined, 0xa0f /* Entry should be defined */);
            index = baseIndex;
            baseNodeChange = baseEntry[1];
        }
        const nodeChange = rebaseChild(newNodeChange, baseNodeChange);
        if (nodeChange !== undefined) {
            rebased.set(index, nodeChange);
        }
        nextIndex = index + 1;
    }
    return rebased;
}
function pruneGenericChange(changeset, pruneChild) {
    const pruned = new sorted_btree_es6_1.BTree();
    for (const [index, node] of changeset.entries()) {
        const prunedNode = pruneChild(node);
        if (prunedNode !== undefined) {
            pruned.set(index, node);
        }
    }
    return pruned;
}
function replaceRevisions(changeset, oldRevisions, newRevision) {
    return changeset.mapValues((node) => (0, index_js_1.replaceAtomRevisions)(node, oldRevisions, newRevision));
}
/**
 * {@link FieldKind} used to represent changes to elements of a field in a field-kind-agnostic format.
 */
exports.genericFieldKind = new fieldKindWithEditor_js_1.FieldKindWithEditor("ModularEditBuilder.Generic", index_js_1.Multiplicity.Sequence, exports.genericChangeHandler, (types, other) => false, new Set());
/**
 * Converts a {@link GenericChangeset} into a field-kind-specific `TChange`.
 * @param changeset - The generic changeset to convert.
 * @param target - The {@link FieldChangeHandler} for the `FieldKind` that the returned change should target.
 * @param composeChild - A delegate to compose {@link NodeChangeset}s.
 * @returns An equivalent changeset as represented by the `target` field-kind.
 */
function convertGenericChange(changeset, target) {
    return target.editor.buildChildChanges(changeset.entries());
}
exports.convertGenericChange = convertGenericChange;
function* relevantRemovedRoots(change, relevantRemovedRootsFromChild) {
    for (const nodeChange of change.values()) {
        yield* relevantRemovedRootsFromChild(nodeChange);
    }
}
//# sourceMappingURL=genericFieldKind.js.map