/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { fail, unreachableCase } from "@fluidframework/core-utils/internal";
import { makeCodecFamily, makeVersionDispatchingCodec, makeVersionedValidatedCodec, } from "../../codec/index.js";
import { SchemaVersion, decodeFieldSchema, encodeFieldSchemaV1, encodeFieldSchemaV2, storedSchemaDecodeDispatcher, } from "../../core/index.js";
import { brand } from "../../util/index.js";
import { Format as FormatV1 } from "./formatV1.js";
import { Format as FormatV2 } from "./formatV2.js";
/**
 * Convert a FluidClientVersion to a SchemaVersion.
 * @param clientVersion - The FluidClientVersion to convert.
 * @returns The SchemaVersion that corresponds to the provided FluidClientVersion.
 */
export function clientVersionToSchemaVersion(clientVersion) {
    // Only one version of the schema codec is currently supported.
    return SchemaVersion.v1;
}
/**
 * Create a schema codec.
 * @param options - Specifies common codec options, including which `validator` to use.
 * @param writeVersion - The schema write version.
 * @returns The composed codec.
 *
 * @privateRemarks We should consider using the Shared Tree format version instead as it may be more valuable for application authors than the schema version.
 */
export function makeSchemaCodec(options, writeVersion) {
    const family = makeSchemaCodecs(options);
    return makeVersionDispatchingCodec(family, { ...options, writeVersion });
}
/**
 * Create a family of schema codecs.
 * @param options - Specifies common codec options, including which `validator` to use.
 * @returns The composed codec family.
 */
export function makeSchemaCodecs(options) {
    return makeCodecFamily([
        [SchemaVersion.v1, makeSchemaCodecV1(options)],
        [SchemaVersion.v2, makeSchemaCodecV2(options)],
    ]);
}
/**
 * Encode an in-memory TreeStoredSchema into the specified format version.
 * @param repo - The in-memory schema.
 * @param version - The schema write version.
 * @returns The encoded schema.
 */
export function encodeRepo(repo, version) {
    switch (version) {
        case SchemaVersion.v1:
            return encodeRepoV1(repo);
        case SchemaVersion.v2:
            return encodeRepoV2(repo);
        default:
            unreachableCase(version);
    }
}
function encodeRepoV1(repo) {
    const nodeSchema = encodeNodeSchema(repo, (schema) => schema.encodeV1());
    const rootFieldSchema = encodeFieldSchemaV1(repo.rootFieldSchema);
    return {
        version: SchemaVersion.v1,
        nodes: nodeSchema,
        root: rootFieldSchema,
    };
}
function encodeRepoV2(repo) {
    const nodeSchema = encodeNodeSchema(repo, (schema) => schema.encodeV2());
    const rootFieldSchema = encodeFieldSchemaV2(repo.rootFieldSchema);
    return {
        version: SchemaVersion.v2,
        nodes: nodeSchema,
        root: rootFieldSchema,
    };
}
/**
 * Shared logic for encoding node schemas.
 * @param repo - The stored schema to encode.
 * @param encodeValue - A function which encodes a single node schema.
 * @returns The encoded node schema.
 */
function encodeNodeSchema(repo, encodeValue) {
    const nodeSchema = Object.create(null);
    for (const name of [...repo.nodeSchema.keys()].sort()) {
        const schema = repo.nodeSchema.get(name) ?? fail(0xb28 /* missing schema */);
        Object.defineProperty(nodeSchema, name, {
            enumerable: true,
            configurable: true,
            writable: true,
            value: encodeValue(schema),
        });
    }
    return nodeSchema;
}
function decodeV1(f) {
    const nodeSchema = new Map();
    for (const [key, schema] of Object.entries(f.nodes)) {
        const storedSchemaDecoder = storedSchemaDecodeDispatcher.dispatch(schema);
        // No metadata in v1, so pass undefined
        nodeSchema.set(brand(key), storedSchemaDecoder(undefined));
    }
    return {
        rootFieldSchema: decodeFieldSchema(f.root),
        nodeSchema,
    };
}
function decodeV2(f) {
    const nodeSchema = new Map();
    for (const [key, schema] of Object.entries(f.nodes)) {
        const storedSchemaDecoder = storedSchemaDecodeDispatcher.dispatch(schema.kind);
        // Pass in the node metadata
        nodeSchema.set(brand(key), storedSchemaDecoder(schema.metadata));
    }
    return {
        rootFieldSchema: decodeFieldSchema(f.root),
        nodeSchema,
    };
}
/**
 * Creates a codec which performs synchronous monolithic encoding of schema content.
 * @param options - Specifies common codec options, including which `validator` to use.
 * @returns The codec.
 */
function makeSchemaCodecV1(options) {
    return makeVersionedValidatedCodec(options, new Set([SchemaVersion.v1]), FormatV1, {
        encode: (data) => encodeRepoV1(data),
        decode: (data) => decodeV1(data),
    });
}
/**
 * Creates a codec which performs synchronous monolithic encoding of schema content.
 * @param options - Specifies common codec options, including which `validator` to use.
 * @returns The codec.
 */
function makeSchemaCodecV2(options) {
    return makeVersionedValidatedCodec(options, new Set([SchemaVersion.v2]), FormatV2, {
        encode: (data) => encodeRepoV2(data),
        decode: (data) => decodeV2(data),
    });
}
//# sourceMappingURL=codec.js.map