"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeFieldShapes = exports.NodeShape = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/id-compressor/internal");
const index_js_1 = require("../../../core/index.js");
const index_js_2 = require("../../../util/index.js");
const chunkEncodingGeneric_js_1 = require("./chunkEncodingGeneric.js");
const compressedEncode_js_1 = require("./compressedEncode.js");
class NodeShape extends chunkEncodingGeneric_js_1.Shape {
    constructor(type, value, 
    /**
     * Encoders for a specific set of fields, by key, in the order they will be encoded.
     * These are fields for which specialized encoding is provided as an optimization.
     * Using these for a given field instead of falling back to {@link NodeShape.specializedFieldEncoders} is often more efficient:
     * this avoids the need to explicitly include the key and shape in the encoded data for each node instance.
     * Instead, this information is here, and thus is encoded only once as part of the node shape.
     * These encoders will be used, even if the field they apply to is empty (which can add overhead for fields which are usually empty).
     *
     * Any fields not included here will be encoded using {@link NodeShape.otherFieldsEncoder}.
     * If {@link NodeShape.otherFieldsEncoder} is undefined, then this must handle all non-empty fields.
     */
    specializedFieldEncoders, 
    /**
     * Encoder for all other fields that are not in {@link NodeShape.specializedFieldEncoders}. These fields must
     * be encoded after the specialized fields.
     */
    otherFieldsEncoder) {
        super();
        this.type = type;
        this.value = value;
        this.specializedFieldEncoders = specializedFieldEncoders;
        this.otherFieldsEncoder = otherFieldsEncoder;
        this.specializedFieldKeys = new Set(this.specializedFieldEncoders.map((f) => f.key));
    }
    getValueToEncode(cursor, cache) {
        if (this.value === 0) {
            (0, internal_1.assert)(typeof cursor.value === "string", 0x9aa /* identifier must be type string */);
            if ((0, internal_2.isStableId)(cursor.value)) {
                const sessionSpaceCompressedId = cache.idCompressor.tryRecompress(cursor.value);
                if (sessionSpaceCompressedId !== undefined) {
                    return cache.idCompressor.normalizeToOpSpace(sessionSpaceCompressedId);
                }
            }
        }
        return cursor.value;
    }
    encodeNode(cursor, cache, outputBuffer) {
        if (this.type === undefined) {
            outputBuffer.push(new chunkEncodingGeneric_js_1.IdentifierToken(cursor.type));
        }
        else {
            (0, internal_1.assert)(cursor.type === this.type, 0x741 /* type must match shape */);
        }
        (0, compressedEncode_js_1.encodeValue)(this.getValueToEncode(cursor, cache), this.value, outputBuffer);
        for (const fieldEncoder of this.specializedFieldEncoders) {
            cursor.enterField((0, index_js_2.brand)(fieldEncoder.key));
            fieldEncoder.encoder.encodeField(cursor, cache, outputBuffer);
            cursor.exitField();
        }
        const otherFieldsBuffer = [];
        (0, index_js_1.forEachField)(cursor, () => {
            const key = cursor.getFieldKey();
            if (!this.specializedFieldKeys.has(key)) {
                (0, internal_1.assert)(this.otherFieldsEncoder !== undefined, 0x742 /* had extra local fields when shape does not support them */);
                otherFieldsBuffer.push(new chunkEncodingGeneric_js_1.IdentifierToken(key));
                this.otherFieldsEncoder.encodeField(cursor, cache, otherFieldsBuffer);
            }
        });
        if (this.otherFieldsEncoder !== undefined) {
            outputBuffer.push(otherFieldsBuffer);
        }
    }
    encodeShape(identifiers, shapes) {
        return {
            c: {
                type: encodeOptionalIdentifier(this.type, identifiers),
                value: this.value,
                fields: encodeFieldShapes(this.specializedFieldEncoders, identifiers, shapes),
                extraFields: encodeOptionalFieldShape(this.otherFieldsEncoder, shapes),
            },
        };
    }
    countReferencedShapesAndIdentifiers(identifiers, shapeDiscovered) {
        if (this.type !== undefined) {
            identifiers.add(this.type);
        }
        for (const fieldEncoder of this.specializedFieldEncoders) {
            identifiers.add(fieldEncoder.key);
            shapeDiscovered(fieldEncoder.encoder.shape);
        }
        if (this.otherFieldsEncoder !== undefined) {
            shapeDiscovered(this.otherFieldsEncoder.shape);
        }
    }
    get shape() {
        return this;
    }
}
exports.NodeShape = NodeShape;
function encodeFieldShapes(fieldEncoders, identifiers, shapes) {
    if (fieldEncoders.length === 0) {
        return undefined;
    }
    return fieldEncoders.map((fieldEncoder) => [
        // key
        encodeIdentifier(fieldEncoder.key, identifiers),
        // shape
        shapes.valueToIndex.get(fieldEncoder.encoder.shape) ?? (0, internal_1.fail)(0xb50 /* missing shape */),
    ]);
}
exports.encodeFieldShapes = encodeFieldShapes;
function encodeIdentifier(identifier, identifiers) {
    return identifiers.valueToIndex.get(identifier) ?? identifier;
}
function encodeOptionalIdentifier(identifier, identifiers) {
    return identifier === undefined ? undefined : encodeIdentifier(identifier, identifiers);
}
function encodeOptionalFieldShape(shape, shapes) {
    return shape === undefined ? undefined : dedupShape(shape.shape, shapes);
}
function dedupShape(shape, shapes) {
    return shapes.valueToIndex.get(shape) ?? (0, internal_1.fail)(0xb51 /* missing shape */);
}
//# sourceMappingURL=nodeShape.js.map