/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
import { getConfigForMinVersionForCollab, lowestMinVersionForCollab, } from "@fluidframework/runtime-utils/internal";
import { FluidClientVersion, makeVersionedValidatedCodec, } from "../../../codec/index.js";
import { brand, brandedNumberType, } from "../../../util/index.js";
import { TreeCompressionStrategy } from "../../treeCompressionUtils.js";
import { decode } from "./chunkDecoding.js";
import { validVersions, FieldBatchFormatVersion, EncodedFieldBatchV1, EncodedFieldBatchV2, } from "./format.js";
import { uncompressedEncodeV1, uncompressedEncodeV2 } from "./uncompressedEncode.js";
import { schemaCompressedEncodeV1, schemaCompressedEncodeV2 } from "./schemaBasedEncode.js";
const ChunkReferenceId = brandedNumberType({ multipleOf: 1, minimum: 0 });
/**
 * Convert a MinimumVersionForCollab to write version for {@link FieldBatchCodec}.
 * @param clientVersion - The MinimumVersionForCollab to convert.
 */
function clientVersionToFieldBatchVersion(clientVersion) {
    return brand(getConfigForMinVersionForCollab(clientVersion, {
        [lowestMinVersionForCollab]: FieldBatchFormatVersion.v1,
        [FluidClientVersion.v2_73]: FieldBatchFormatVersion.v2,
    }));
}
export function makeFieldBatchCodec(options) {
    const writeVersion = clientVersionToFieldBatchVersion(options.minVersionForCollab);
    // Note: it's important that the decode function is schema-agnostic for this strategy/layering to work, since
    // the schema that an op was encoded in doesn't necessarily match the current schema for the document (e.g. if
    // decode is being run on a client that just submitted a schema change, but the op is from another client who has
    // yet to receive that change).
    assert(validVersions.has(writeVersion), 0x935 /* Invalid write version for FieldBatch codec */);
    let uncompressedEncodeFn;
    let schemaCompressedEncodeFn;
    let encodedFieldBatchType;
    switch (writeVersion) {
        case FieldBatchFormatVersion.v1:
            uncompressedEncodeFn = uncompressedEncodeV1;
            schemaCompressedEncodeFn = schemaCompressedEncodeV1;
            encodedFieldBatchType = EncodedFieldBatchV1;
            break;
        case FieldBatchFormatVersion.v2:
            uncompressedEncodeFn = uncompressedEncodeV2;
            schemaCompressedEncodeFn = schemaCompressedEncodeV2;
            encodedFieldBatchType = EncodedFieldBatchV2;
            break;
        default:
            unreachableCase(writeVersion);
    }
    return makeVersionedValidatedCodec(options, validVersions, encodedFieldBatchType, {
        encode: (data, context) => {
            for (const cursor of data) {
                assert(cursor.mode === 1 /* CursorLocationType.Fields */, 0x8a3 /* FieldBatch expects fields cursors */);
            }
            let encoded;
            let incrementalEncoder;
            switch (context.encodeType) {
                case TreeCompressionStrategy.Uncompressed:
                    encoded = uncompressedEncodeFn(data);
                    break;
                case TreeCompressionStrategy.CompressedIncremental:
                    assert(writeVersion >= FieldBatchFormatVersion.v2, 0xca0 /* Unsupported FieldBatchFormatVersion for incremental encoding; must be v2 or higher */);
                    // Incremental encoding is only supported for CompressedIncremental.
                    incrementalEncoder = context.incrementalEncoderDecoder;
                case TreeCompressionStrategy.Compressed:
                    // eslint-disable-next-line unicorn/prefer-ternary
                    if (context.schema !== undefined) {
                        encoded = schemaCompressedEncodeFn(context.schema.schema, context.schema.policy, data, context.idCompressor, incrementalEncoder);
                    }
                    else {
                        // TODO: consider enabling a somewhat compressed but not schema accelerated encode.
                        encoded = uncompressedEncodeFn(data);
                    }
                    break;
                default:
                    unreachableCase(context.encodeType);
            }
            // TODO: consider checking input data was in schema.
            return encoded;
        },
        decode: (data, context) => {
            // TODO: consider checking data is in schema.
            return decode(data, {
                idCompressor: context.idCompressor,
                originatorId: context.originatorId,
            }, context.incrementalEncoderDecoder).map((chunk) => chunk.cursor());
        },
    });
}
export function getCodecTreeForFieldBatchFormat(clientVersion) {
    return { name: "FieldBatch", version: clientVersionToFieldBatchVersion(clientVersion) };
}
//# sourceMappingURL=codecs.js.map