"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.oneFromSet = exports.treeShaper = exports.fieldShaper = exports.buildCache = exports.schemaCompressedEncode = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../../../core/index.js");
const index_js_2 = require("../../../util/index.js");
const compressedEncode_js_1 = require("./compressedEncode.js");
const format_js_1 = require("./format.js");
const nodeShape_js_1 = require("./nodeShape.js");
/**
 * Encode data from `fieldBatch` in into an `EncodedChunk`.
 *
 * Optimized for encoded size and encoding performance.
 * TODO: This function should eventually also take in the root FieldSchema to more efficiently compress the nodes.
 */
function schemaCompressedEncode(schema, policy, fieldBatch, idCompressor) {
    return (0, compressedEncode_js_1.compressedEncode)(fieldBatch, buildCache(schema, policy, idCompressor));
}
exports.schemaCompressedEncode = schemaCompressedEncode;
function buildCache(schema, policy, idCompressor) {
    const cache = new compressedEncode_js_1.EncoderCache((fieldHandler, schemaName) => treeShaper(schema, policy, fieldHandler, schemaName), (treeHandler, field) => fieldShaper(treeHandler, field, cache, schema), policy.fieldKinds, idCompressor);
    return cache;
}
exports.buildCache = buildCache;
/**
 * Selects shapes to use to encode fields.
 */
function fieldShaper(treeHandler, field, cache, storedSchema) {
    const kind = cache.fieldShapes.get(field.kind) ?? (0, index_js_2.fail)("missing FieldKind");
    const type = oneFromSet(field.types);
    const nodeEncoder = type !== undefined ? treeHandler.shapeFromTree(type) : compressedEncode_js_1.anyNodeEncoder;
    if (kind.multiplicity === index_js_1.Multiplicity.Single) {
        if (field.kind === index_js_1.identifierFieldKindIdentifier) {
            (0, internal_1.assert)(type !== undefined, 0x999 /* field type must be defined in identifier field */);
            const nodeSchema = storedSchema.nodeSchema.get(type);
            (0, internal_1.assert)(nodeSchema !== undefined, 0x99a /* nodeSchema must be defined */);
            (0, internal_1.assert)(nodeSchema instanceof index_js_1.LeafNodeStoredSchema, 0x99b /* nodeSchema must be LeafNodeStoredSchema */);
            (0, internal_1.assert)(nodeSchema.leafValue === index_js_1.ValueSchema.String, 0x99c /* identifier field can only be type string */);
            const identifierNodeEncoder = new nodeShape_js_1.NodeShape(type, format_js_1.SpecialField.Identifier, [], undefined);
            return (0, compressedEncode_js_1.asFieldEncoder)(identifierNodeEncoder);
        }
        return (0, compressedEncode_js_1.asFieldEncoder)(nodeEncoder);
    }
    else {
        return cache.nestedArray(nodeEncoder);
    }
}
exports.fieldShaper = fieldShaper;
/**
 * Selects shapes to use to encode trees.
 */
function treeShaper(fullSchema, policy, fieldHandler, schemaName) {
    const schema = fullSchema.nodeSchema.get(schemaName) ?? (0, index_js_2.fail)("missing node schema");
    if (schema instanceof index_js_1.ObjectNodeStoredSchema) {
        // TODO:Performance:
        // consider moving some optional and sequence fields to extra fields if they are commonly empty
        // to reduce encoded size.
        const objectNodeFields = [];
        for (const [key, field] of schema.objectNodeFields ?? []) {
            objectNodeFields.push({ key, shape: fieldHandler.shapeFromField(field) });
        }
        const shape = new nodeShape_js_1.NodeShape(schemaName, false, objectNodeFields, undefined);
        return shape;
    }
    if (schema instanceof index_js_1.LeafNodeStoredSchema) {
        const shape = new nodeShape_js_1.NodeShape(schemaName, valueShapeFromSchema(schema.leafValue), [], undefined);
        return shape;
    }
    if (schema instanceof index_js_1.MapNodeStoredSchema) {
        const shape = new nodeShape_js_1.NodeShape(schemaName, false, [], fieldHandler.shapeFromField(schema.mapFields));
        return shape;
    }
    (0, index_js_2.fail)("unsupported node kind");
}
exports.treeShaper = treeShaper;
function oneFromSet(set) {
    if (set === undefined) {
        return undefined;
    }
    if (set.size !== 1) {
        return undefined;
    }
    for (const item of set) {
        return item;
    }
}
exports.oneFromSet = oneFromSet;
function valueShapeFromSchema(schema) {
    switch (schema) {
        case undefined:
            return false;
        case index_js_1.ValueSchema.Number:
        case index_js_1.ValueSchema.String:
        case index_js_1.ValueSchema.Boolean:
        case index_js_1.ValueSchema.FluidHandle:
            return true;
        case index_js_1.ValueSchema.Null:
            return [null];
        default:
            (0, internal_1.unreachableCase)(schema);
    }
}
//# sourceMappingURL=schemaBasedEncoding.js.map