/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert } from "@fluidframework/common-utils";
import { forEachField, } from "../../../core";
import { brand, fail } from "../../../util";
import { IdentifierToken, Shape } from "./chunkEncodingGeneric";
import { encodeValue, } from "./compressedEncode";
export class NodeShape extends Shape {
    constructor(type, value, fields, extraLocal) {
        super();
        this.type = type;
        this.value = value;
        this.fields = fields;
        this.extraLocal = extraLocal;
        this.explicitKeys = new Set(this.fields.map((f) => f.key));
    }
    encodeNode(cursor, cache, outputBuffer) {
        if (this.type === undefined) {
            outputBuffer.push(new IdentifierToken(cursor.type));
        }
        else {
            assert(cursor.type === this.type, 0x741 /* type must match shape */);
        }
        encodeValue(cursor.value, this.value, outputBuffer);
        for (const field of this.fields) {
            cursor.enterField(brand(field.key));
            field.shape.encodeField(cursor, cache, outputBuffer);
            cursor.exitField();
        }
        const localBuffer = [];
        forEachField(cursor, () => {
            const key = cursor.getFieldKey();
            if (!this.explicitKeys.has(key)) {
                assert(this.extraLocal !== undefined, 0x742 /* had extra local fields when shape does not support them */);
                localBuffer.push(new IdentifierToken(key));
                this.extraLocal.encodeField(cursor, cache, localBuffer);
            }
        });
        if (this.extraLocal !== undefined) {
            outputBuffer.push(localBuffer);
        }
    }
    encodeShape(identifiers, shapes) {
        return {
            c: {
                type: encodeOptionalIdentifier(this.type, identifiers),
                value: this.value,
                fields: encodeFieldShapes(this.fields, identifiers, shapes),
                extraFields: encodeOptionalFieldShape(this.extraLocal, shapes),
            },
        };
    }
    count(identifiers, shapes) {
        if (this.type !== undefined) {
            identifiers.add(this.type);
        }
        for (const field of this.fields) {
            identifiers.add(field.key);
            shapes(field.shape.shape);
        }
        if (this.extraLocal !== undefined) {
            shapes(this.extraLocal.shape);
        }
    }
    get shape() {
        return this;
    }
}
export function encodeFieldShapes(fields, identifiers, shapes) {
    return fields.map((field) => ({
        key: encodeIdentifier(field.key, identifiers),
        shape: shapes.valueToIndex.get(field.shape.shape) ?? fail("missing shape"),
    }));
}
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) ?? fail("missing shape");
}
//# sourceMappingURL=nodeShape.js.map