"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.newGenericChangeset = exports.convertGenericChange = exports.genericFieldKind = exports.genericChangeHandler = void 0;
const index_js_1 = require("../../core/index.js");
const index_js_2 = require("../../util/index.js");
const multiplicity_js_1 = require("../multiplicity.js");
const fieldKindWithEditor_js_1 = require("./fieldKindWithEditor.js");
const genericFieldKindCodecs_js_1 = require("./genericFieldKindCodecs.js");
/**
 * {@link FieldChangeHandler} implementation for {@link GenericChangeset}.
 */
exports.genericChangeHandler = {
    rebaser: {
        compose: (change1, change2, composeChildren) => {
            const composed = [];
            let listIndex1 = 0;
            let listIndex2 = 0;
            while (listIndex1 < change1.change.length || listIndex2 < change2.change.length) {
                const next1 = change1.change[listIndex1];
                const next2 = change2.change[listIndex2];
                const nodeIndex1 = next1?.index ?? Infinity;
                const nodeIndex2 = next2?.index ?? Infinity;
                if (nodeIndex1 < nodeIndex2) {
                    composed.push({
                        index: nodeIndex1,
                        nodeChange: composeChildren(next1.nodeChange, undefined),
                    });
                    listIndex1 += 1;
                }
                else if (nodeIndex2 < nodeIndex1) {
                    composed.push({
                        index: nodeIndex2,
                        nodeChange: composeChildren(undefined, next2.nodeChange),
                    });
                    listIndex2 += 1;
                }
                else {
                    // Both nodes are at the same position.
                    composed.push({
                        index: nodeIndex1,
                        nodeChange: composeChildren(next1.nodeChange, next2.nodeChange),
                    });
                    listIndex1 += 1;
                    listIndex2 += 1;
                }
            }
            return composed;
        },
        invert: ({ change }, invertChild) => {
            return change.map(({ index, nodeChange }) => ({
                index,
                nodeChange: invertChild(nodeChange),
            }));
        },
        rebase: rebaseGenericChange,
        prune: pruneGenericChange,
    },
    codecsFactory: genericFieldKindCodecs_js_1.makeGenericChangeCodec,
    editor: {
        buildChildChange(index, change) {
            return [{ index, nodeChange: change }];
        },
    },
    intoDelta: ({ change }, deltaFromChild) => {
        let nodeIndex = 0;
        const markList = [];
        for (const { index, nodeChange } of change) {
            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,
    createEmpty: () => [],
};
function rebaseGenericChange(change, { change: over }, rebaseChild) {
    const rebased = [];
    let iChange = 0;
    let iOver = 0;
    while (iChange < change.length || iOver < over.length) {
        const a = change[iChange];
        const b = over[iOver];
        const aIndex = a?.index ?? Infinity;
        const bIndex = b?.index ?? Infinity;
        let nodeChangeA;
        let nodeChangeB;
        let index;
        if (aIndex === bIndex) {
            index = a.index;
            nodeChangeA = a.nodeChange;
            nodeChangeB = b.nodeChange;
            iChange += 1;
            iOver += 1;
        }
        else if (aIndex < bIndex) {
            index = a.index;
            nodeChangeA = a.nodeChange;
            iChange += 1;
        }
        else {
            index = b.index;
            nodeChangeB = b.nodeChange;
            iOver += 1;
        }
        const nodeChange = rebaseChild(nodeChangeA, nodeChangeB);
        if (nodeChange !== undefined) {
            rebased.push({
                index,
                nodeChange,
            });
        }
    }
    return rebased;
}
function pruneGenericChange(changeset, pruneChild) {
    const pruned = [];
    for (const change of changeset) {
        const prunedNode = pruneChild(change.nodeChange);
        if (prunedNode !== undefined) {
            pruned.push({ ...change, nodeChange: prunedNode });
        }
    }
    return pruned;
}
/**
 * {@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", multiplicity_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, composeChild, genId, revisionMetadata) {
    const perIndex = changeset.map(({ index, nodeChange }) => (0, index_js_1.makeAnonChange)(target.editor.buildChildChange(index, nodeChange)));
    if (perIndex.length === 0) {
        return target.createEmpty();
    }
    return perIndex.reduce((a, b) => (0, index_js_1.makeAnonChange)(target.rebaser.compose(a, b, composeChild, genId, invalidCrossFieldManager, revisionMetadata))).change;
}
exports.convertGenericChange = convertGenericChange;
const invalidFunc = () => (0, index_js_2.fail)("Should not be called when converting generic changes");
const invalidCrossFieldManager = {
    set: invalidFunc,
    get: invalidFunc,
};
function newGenericChangeset() {
    return [];
}
exports.newGenericChangeset = newGenericChangeset;
function* relevantRemovedRoots({ change }, relevantRemovedRootsFromChild) {
    for (const { nodeChange } of change) {
        yield* relevantRemovedRootsFromChild(nodeChange);
    }
}
//# sourceMappingURL=genericFieldKind.js.map