"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.EndOfTreeSegment = void 0;
const common_utils_1 = require("@fluidframework/common-utils");
const container_utils_1 = require("@fluidframework/container-utils");
const constants_1 = require("./constants");
const mergeTreeNodeWalk_1 = require("./mergeTreeNodeWalk");
/**
 * This is a special segment that is not bound or known by the merge tree itself, but the segment itself pretends
 * to be a removed segment at the end of the tree. It is removed so it appears as a undefined/0 length segment.
 * This segment adds the capability to hold local references that have been detached from the real merge tree,
 * and give the appearance that they exist at the end of the tree. In general, local references only become detached
 * when the tree becomes empty and the EndOfTreeSegment allows us to gracefully handle that case by giving those
 * references a place to live. The EndOfTreeSegment is not in itself a generalized solution to the problem of
 * detached references, as it requires special and careful understanding of its usage. Specifically, it is local only
 * and would not lead to eventually consistent results if serialization of its contained references was attempted,
 * as on deserialization a naive re-insertion would not put the back into the EndOfTreeSegment
 * leading to inconsistent results with existing clients.
 */
class EndOfTreeSegment {
    constructor(segmentOrNode) {
        this.type = "EndOfTreeSegment";
        /*
         * segments must be of at least length one, but
         * removed segments will have a calculated length
         * of undefined/0. we leverage this to create
         * a 0 length segment for the end of the tree
         */
        this.removedSeq = 0;
        this.removedClientIds = [constants_1.LocalClientId];
        this.seq = 0;
        this.clientId = constants_1.LocalClientId;
        this.cachedLength = 1;
        this.addProperties = notSupported;
        this.clone = notSupported;
        this.canAppend = notSupported;
        this.append = notSupported;
        this.splitAt = notSupported;
        this.toJSONObject = notSupported;
        this.ack = notSupported;
        let maybeRoot = segmentOrNode.isLeaf()
            ? segmentOrNode.parent
            : segmentOrNode;
        while ((maybeRoot === null || maybeRoot === void 0 ? void 0 : maybeRoot.parent) !== undefined) {
            maybeRoot = maybeRoot.parent;
        }
        if ((maybeRoot === null || maybeRoot === void 0 ? void 0 : maybeRoot.mergeTree) === undefined) {
            throw new container_utils_1.UsageError("segmentOrNode must be in rooted tree");
        }
        this.mergeTree = maybeRoot.mergeTree;
    }
    isLeaf() {
        return true;
    }
    /**
     * this segment pretends to be a sibling of the last real segment.
     * so compute the necessary properties to pretend to be that segment.
     */
    getEndSegProps() {
        var _a;
        let lastSegment;
        let depth = 1;
        const root = this.mergeTree.root;
        (0, mergeTreeNodeWalk_1.depthFirstNodeWalk)(root, root.children[root.childCount - 1], (node) => {
            depth++;
            if (node === null || node === void 0 ? void 0 : node.isLeaf()) {
                lastSegment = node;
                return mergeTreeNodeWalk_1.NodeAction.Exit;
            }
        }, undefined, undefined, false);
        const parent = (_a = lastSegment === null || lastSegment === void 0 ? void 0 : lastSegment.parent) !== null && _a !== void 0 ? _a : root;
        const index = parent.childCount;
        return {
            parent,
            index,
            depth,
        };
    }
    get parent() {
        return this.getEndSegProps().parent;
    }
    get index() {
        return this.getEndSegProps().index;
    }
    get ordinal() {
        // just compute an arbitrarily big ordinal
        // we base it on the depth of the tree
        // to ensure it is bigger than all ordinals in
        // the tree, as each layer appends to the previous
        return String.fromCharCode(0xffff).repeat(this.getEndSegProps().depth);
    }
    /*
     * since this segment isn't real, throw on any segment
     * operation that isn't expected
     */
    get segmentGroups() {
        return notSupported();
    }
    get trackingCollection() {
        return notSupported();
    }
}
exports.EndOfTreeSegment = EndOfTreeSegment;
const notSupported = () => {
    (0, common_utils_1.assert)(false, 0x3ed /* operation not supported */);
};
//# sourceMappingURL=endOfTreeSegment.js.map