"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.overwriteInfo = exports.wasRemovedOnInsert = exports.removeRemovalInfo = exports.assertRemoved = exports.isRemoved = exports.toRemovalInfo = exports.removeMergeNodeInfo = exports.assertMergeNode = exports.isMergeNodeInfo = exports.toMergeNodeInfo = exports.assertInserted = exports.isInsideObliterate = exports.isInserted = exports.toInsertionInfo = exports.propInstanceOf = exports.hasProp = exports.propExists = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const constants_js_1 = require("./constants.js");
const mergeTreeNodes_js_1 = require("./mergeTreeNodes.js");
function propExists(thing, prop) {
    return (0, internal_1.isObject)(thing) && prop in thing;
}
exports.propExists = propExists;
function hasProp(thing, prop, type) {
    return (propExists(thing, prop) &&
        (type === "array" ? Array.isArray(thing[prop]) : typeof thing[prop] === type));
}
exports.hasProp = hasProp;
function propInstanceOf(thing, prop, type) {
    return propExists(thing, prop) && thing[prop] instanceof type;
}
exports.propInstanceOf = propInstanceOf;
/**
 * Converts a segment-like object to an insertion info object if possible.
 *
 * @param segmentLike - The segment-like object to convert.
 * @returns The insertion info object if the conversion is possible, otherwise undefined.
 */
const toInsertionInfo = (segmentLike) => {
    return segmentLike !== undefined &&
        hasProp(segmentLike, "insert", "object") &&
        hasProp(segmentLike.insert, "clientId", "number") &&
        hasProp(segmentLike.insert, "seq", "number")
        ? segmentLike
        : undefined;
};
exports.toInsertionInfo = toInsertionInfo;
/**
 * A type-guard which determines if the segment has insertion info, and
 * returns true if it does, along with applying strong typing.
 *
 * @param segmentLike - The segment-like object to check.
 * @returns True if the segment has insertion info, otherwise false.
 */
const isInserted = (segmentLike) => (0, exports.toInsertionInfo)(segmentLike) !== undefined;
exports.isInserted = isInserted;
const isInsideObliterate = (segmentLike) => segmentLike !== undefined && hasProp(segmentLike, "obliteratePrecedingInsertion", "object");
exports.isInsideObliterate = isInsideObliterate;
/**
 * Asserts that the segment has insertion info. Usage of this function should not produce a user facing error.
 *
 * @param segmentLike - The segment-like object to check.
 * @throws Will throw an error if the segment does not have insertion info.
 */
const assertInserted = (segmentLike) => (0, internal_1.assert)(segmentLike === undefined || (0, exports.isInserted)(segmentLike), 0xaa0 /* must be insertionInfo */);
exports.assertInserted = assertInserted;
/**
 * Converts a segment-like object to a merge node info object if possible.
 *
 * @param segmentLike - The segment-like object to convert.
 * @returns The merge node info object if the conversion is possible, otherwise undefined.
 */
const toMergeNodeInfo = (nodeLike) => propInstanceOf(nodeLike, "parent", mergeTreeNodes_js_1.MergeBlock) &&
    hasProp(nodeLike, "ordinal", "string") &&
    hasProp(nodeLike, "index", "number")
    ? nodeLike
    : undefined;
exports.toMergeNodeInfo = toMergeNodeInfo;
/**
 * A type-guard which determines if the segment has merge node info, and
 * returns true if it does, along with applying strong typing.
 *
 * @param nodeLike - The segment-like object to check.
 * @returns True if the segment has merge node info, otherwise false.
 */
const isMergeNodeInfo = (nodeLike) => (0, exports.toMergeNodeInfo)(nodeLike) !== undefined;
exports.isMergeNodeInfo = isMergeNodeInfo;
/**
 * Asserts that the segment has merge node info. Usage of this function should not produce a user facing error.
 *
 * @param segmentLike - The segment-like object to check.
 * @throws Will throw an error if the segment does not have merge node info.
 */
const assertMergeNode = (segmentLike) => (0, internal_1.assert)(segmentLike === undefined || (0, exports.isMergeNodeInfo)(segmentLike), 0xaa1 /* must be MergeNodeInfo */);
exports.assertMergeNode = assertMergeNode;
/**
 * Removes the merge node info. This is used to remove nodes from the merge-tree.
 * @param segmentLike - The segment-like object to check.
 * @returns This function will change the type of the provided node like to never via an assertion. This
 * ensures no further usage of the removed merge node info is allowed. if continued use is required other
 * type coercion methods should be used to correctly re-type the variable.
 */
const removeMergeNodeInfo = (nodeLike) => Object.assign(nodeLike, {
    parent: undefined,
    index: undefined,
    ordinal: undefined,
});
exports.removeMergeNodeInfo = removeMergeNodeInfo;
/**
 * Converts a segment-like object to a removal info object if possible.
 *
 * @param segmentLike - The segment-like object to convert.
 * @returns The removal info object if the conversion is possible, otherwise undefined.
 */
const toRemovalInfo = (segmentLike) => {
    return hasProp(segmentLike, "removes", "array") &&
        segmentLike.removes.length > 0 &&
        hasProp(segmentLike.removes[0], "clientId", "number") &&
        hasProp(segmentLike.removes[0], "seq", "number")
        ? segmentLike
        : undefined;
};
exports.toRemovalInfo = toRemovalInfo;
/**
 * A type-guard which determines if the segment has removal info, and
 * returns true if it does, along with applying strong typing.
 *
 * @param segmentLike - The segment-like object to check.
 * @returns True if the segment has removal info, otherwise false.
 */
// export const isRemoved = (segmentLike: unknown): segmentLike is IHasRemovalInfo =>
// 	toRemovalInfo(segmentLike) !== undefined;
const isRemoved = (segmentLike) => (0, exports.toRemovalInfo)(segmentLike) !== undefined;
exports.isRemoved = isRemoved;
/**
 * Asserts that the segment has removal info. Usage of this function should not produce a user facing error.
 *
 * @param segmentLike - The segment-like object to check.
 * @throws Will throw an error if the segment does not have removal info.
 */
const assertRemoved = (segmentLike) => (0, internal_1.assert)(segmentLike === undefined || (0, exports.isRemoved)(segmentLike), 0xaa2 /* must be removalInfo */);
exports.assertRemoved = assertRemoved;
/**
 * Removes the removal info. This is used in rollback.
 * @param segmentLike - The segment-like object to check.
 * @returns This function will change the type of the provided node like to never via an assertion. This
 * ensures no further usage of the removed removal info is allowed. if continued use is required other
 * type coercion methods should be use to correctly re-type the variable.
 */
const removeRemovalInfo = (nodeLike) => Object.assign(nodeLike, {
    removes: undefined,
});
exports.removeRemovalInfo = removeRemovalInfo;
/**
 * Returns whether this segment was marked removed as soon as its insertion was acked.
 *
 * This can happen when an an insert occurs concurrent to an obliterate over the range the segment was inserted into,
 * and the obliterate was sequenced first.
 *
 * When this happens, the segment is only ever visible to the client that inserted the segment
 * (and only until that client has seen the obliterate which removed their segment).
 */
function wasRemovedOnInsert(segment) {
    const removeInfo = (0, exports.toRemovalInfo)(segment);
    const removedSeq = removeInfo?.removes[0].seq;
    if (removedSeq === undefined || removedSeq === constants_js_1.UnassignedSequenceNumber) {
        return false;
    }
    const insertSeq = segment.insert.seq;
    return insertSeq === constants_js_1.UnassignedSequenceNumber || insertSeq > removedSeq;
}
exports.wasRemovedOnInsert = wasRemovedOnInsert;
/**
 * Overwrites the segment info on a segment-like object.
 *
 * @param segmentLike - The segment-like object to set the info on.
 * @param info - The segment info to overwrite.
 * @returns The segment-like object with the info set.
 */
const overwriteInfo = (segmentLike, info) => Object.assign(segmentLike, info);
exports.overwriteInfo = overwriteInfo;
//# sourceMappingURL=segmentInfos.js.map