"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useStrictPartialLengthChecks = exports.validateRefCount = exports.validatePartialLengths = exports.countOperations = exports.nodeOrdinalsHaveIntegrity = exports.obliterateRange = exports.markRangeRemoved = exports.insertSegments = exports.insertText = exports.insertMarker = exports.loadTextFromFileWithMarkers = exports.loadTextFromFile = void 0;
const node_assert_1 = require("node:assert");
const node_fs_1 = __importDefault(require("node:fs"));
const constants_js_1 = require("../constants.js");
const mergeTreeNodeWalk_js_1 = require("../mergeTreeNodeWalk.js");
const mergeTreeNodes_js_1 = require("../mergeTreeNodes.js");
const partialLengths_js_1 = require("../partialLengths.js");
const info = __importStar(require("../segmentInfos.js"));
const textSegment_js_1 = require("../textSegment.js");
const text_js_1 = require("./text.js");
function loadTextFromFile(filename, mergeTree, segLimit = 0) {
    const content = node_fs_1.default.readFileSync(filename, "utf8");
    return (0, text_js_1.loadText)(content, mergeTree, segLimit);
}
exports.loadTextFromFile = loadTextFromFile;
function loadTextFromFileWithMarkers(filename, mergeTree, segLimit = 0) {
    const content = node_fs_1.default.readFileSync(filename, "utf8");
    return (0, text_js_1.loadText)(content, mergeTree, segLimit, true);
}
exports.loadTextFromFileWithMarkers = loadTextFromFileWithMarkers;
function insertMarker({ mergeTree, pos, refSeq, clientId, seq, behaviors, props, opArgs, }) {
    mergeTree.insertSegments(pos, [mergeTreeNodes_js_1.Marker.make(behaviors, props)], refSeq, clientId, seq, opArgs);
}
exports.insertMarker = insertMarker;
function insertText({ mergeTree, pos, refSeq, clientId, seq, text, props, opArgs, }) {
    mergeTree.insertSegments(pos, [textSegment_js_1.TextSegment.make(text, props)], refSeq, clientId, seq, opArgs);
}
exports.insertText = insertText;
function insertSegments({ mergeTree, pos, segments, refSeq, clientId, seq, opArgs, }) {
    mergeTree.insertSegments(pos, segments, refSeq, clientId, seq, opArgs);
}
exports.insertSegments = insertSegments;
function markRangeRemoved({ mergeTree, start, end, refSeq, clientId, seq, opArgs, }) {
    mergeTree.markRangeRemoved(start, end, refSeq, clientId, seq, opArgs);
}
exports.markRangeRemoved = markRangeRemoved;
function obliterateRange({ mergeTree, start, end, refSeq, clientId, seq, opArgs, }) {
    mergeTree.obliterateRange(start, end, refSeq, clientId, seq, opArgs);
}
exports.obliterateRange = obliterateRange;
function nodeOrdinalsHaveIntegrity(block) {
    const olen = block.ordinal.length;
    for (let i = 0; i < block.childCount; i++) {
        if (block.children[i].ordinal) {
            if (olen !== block.children[i].ordinal.length - 1) {
                console.log("node integrity issue");
                return false;
            }
            if (i > 0 && block.children[i].ordinal <= block.children[i - 1].ordinal) {
                console.log("node sib integrity issue");
                return false;
            }
            if (!block.children[i].isLeaf()) {
                return nodeOrdinalsHaveIntegrity(block.children[i]);
            }
        }
        else {
            console.log(`node child ordinal not set ${i}`);
            return false;
        }
    }
    return true;
}
exports.nodeOrdinalsHaveIntegrity = nodeOrdinalsHaveIntegrity;
/**
 * Returns an object that tallies each delta and maintenance operation observed
 * for the given 'mergeTree'.
 */
function countOperations(mergeTree) {
    const counts = {};
    node_assert_1.strict.strictEqual(mergeTree.mergeTreeDeltaCallback, undefined);
    node_assert_1.strict.strictEqual(mergeTree.mergeTreeMaintenanceCallback, undefined);
    const fn = (deltaArgs) => {
        const previous = counts[deltaArgs.operation];
        counts[deltaArgs.operation] = previous === undefined ? 1 : previous + 1;
    };
    mergeTree.mergeTreeDeltaCallback = (opArgs, deltaArgs) => {
        fn(deltaArgs);
    };
    mergeTree.mergeTreeMaintenanceCallback = fn;
    return counts;
}
exports.countOperations = countOperations;
function getPartialLengths(clientId, seq, mergeTree, localSeq, mergeBlock = mergeTree.root) {
    const partialLen = mergeBlock.partialLengths?.getPartialLength(seq, clientId, localSeq);
    let actualLen = 0;
    const isInserted = (segment) => info.isInserted(segment) &&
        ((segment.seq !== constants_js_1.UnassignedSequenceNumber && segment.seq <= seq) ||
            (localSeq !== undefined &&
                segment.seq === constants_js_1.UnassignedSequenceNumber &&
                segment.localSeq !== undefined &&
                segment.localSeq <= localSeq));
    const isRemoved = (segment) => info.isRemoved(segment) &&
        ((localSeq !== undefined &&
            segment.removedSeq === constants_js_1.UnassignedSequenceNumber &&
            segment.localRemovedSeq !== undefined &&
            segment.localRemovedSeq <= localSeq) ||
            (segment.removedSeq !== constants_js_1.UnassignedSequenceNumber && segment.removedSeq <= seq));
    const isMoved = (segment) => info.isMoved(segment) &&
        ((localSeq !== undefined &&
            segment.movedSeq === constants_js_1.UnassignedSequenceNumber &&
            segment.localMovedSeq !== undefined &&
            segment.localMovedSeq <= localSeq) ||
            (segment.movedSeq !== constants_js_1.UnassignedSequenceNumber && segment.movedSeq <= seq));
    (0, mergeTreeNodeWalk_js_1.walkAllChildSegments)(mergeBlock, (segment) => {
        if (isInserted(segment) && !isRemoved(segment) && !isMoved(segment)) {
            actualLen += segment.cachedLength;
        }
        return true;
    });
    return {
        partialLen,
        actualLen,
    };
}
function validatePartialLengths(clientId, mergeTree, expectedValues, localSeq, mergeBlock = mergeTree.root) {
    mergeTree.computeLocalPartials(0);
    for (let i = mergeTree.collabWindow.minSeq + 1; i <= mergeTree.collabWindow.currentSeq; i++) {
        const { partialLen, actualLen } = getPartialLengths(clientId, i, mergeTree, localSeq, mergeBlock);
        if (partialLen && partialLen < 0) {
            node_assert_1.strict.fail("Negative partial length returned");
        }
        node_assert_1.strict.equal(partialLen, actualLen);
    }
    if (!expectedValues) {
        return;
    }
    for (const { seq, len, localSeq: expectedLocalSeq } of expectedValues) {
        const { partialLen, actualLen } = getPartialLengths(clientId, seq, mergeTree, expectedLocalSeq ?? localSeq, mergeBlock);
        node_assert_1.strict.equal(partialLen, len);
        node_assert_1.strict.equal(actualLen, len);
    }
}
exports.validatePartialLengths = validatePartialLengths;
function validateRefCount(collection) {
    if (!collection) {
        return;
    }
    const expectedLength = [...collection].length;
    // eslint-disable-next-line @typescript-eslint/dot-notation
    node_assert_1.strict.equal(collection["refCount"], expectedLength);
}
exports.validateRefCount = validateRefCount;
/**
 * Enable stricter partial length assertions inside tests
 *
 * Note that these assertions can be expensive, and so should not be enabled in
 * production code or tests that run through thousands of ops (e.g. the SharedString
 * fuzz tests).
 */
function useStrictPartialLengthChecks() {
    beforeEach("Enable strict partial lengths", () => {
        partialLengths_js_1.PartialSequenceLengths.options.verifier = partialLengths_js_1.verifyPartialLengths;
        partialLengths_js_1.PartialSequenceLengths.options.verifyExpected = partialLengths_js_1.verifyExpectedPartialLengths;
    });
    afterEach("Disable strict partial lengths", () => {
        partialLengths_js_1.PartialSequenceLengths.options.verifier = undefined;
        partialLengths_js_1.PartialSequenceLengths.options.verifyExpected = undefined;
    });
}
exports.useStrictPartialLengthChecks = useStrictPartialLengthChecks;
//# sourceMappingURL=testUtils.js.map