"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.toSimpleTreeSchema = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const schemaTypes_js_1 = require("../schemaTypes.js");
const index_js_1 = require("../../util/index.js");
const objectNodeTypes_js_1 = require("../objectNodeTypes.js");
const index_js_2 = require("../core/index.js");
/**
 * Converts a "view" schema to a "simple" schema representation.
 */
function toSimpleTreeSchema(schema) {
    const normalizedSchema = (0, schemaTypes_js_1.normalizeFieldSchema)(schema);
    const allowedTypes = allowedTypesFromFieldSchema(normalizedSchema);
    const definitions = new Map();
    populateSchemaDefinitionsForField(normalizedSchema, definitions);
    const output = {
        kind: normalizedSchema.kind,
        allowedTypes,
        definitions,
    };
    (0, index_js_1.copyProperty)(normalizedSchema, "metadata", output);
    return output;
}
exports.toSimpleTreeSchema = toSimpleTreeSchema;
/**
 * Cache in which the results of {@link toSimpleNodeSchema} are saved.
 */
const simpleNodeSchemaCache = new WeakMap();
/**
 * Creates a {@link SimpleNodeSchema} from a {@link TreeNodeSchema}.
 *
 * @remarks Caches the result on the input schema for future calls.
 */
function toSimpleNodeSchema(schema) {
    return (0, index_js_1.getOrCreate)(simpleNodeSchemaCache, schema, () => {
        const kind = schema.kind;
        switch (kind) {
            case index_js_2.NodeKind.Leaf: {
                return leafSchemaToSimpleSchema(schema);
            }
            case index_js_2.NodeKind.Map: {
                return mapSchemaToSimpleSchema(schema);
            }
            case index_js_2.NodeKind.Array: {
                return arraySchemaToSimpleSchema(schema);
            }
            case index_js_2.NodeKind.Object: {
                (0, internal_1.assert)((0, objectNodeTypes_js_1.isObjectNodeSchema)(schema), 0xa06 /* Expected object schema */);
                return objectSchemaToSimpleSchema(schema);
            }
            default: {
                (0, internal_1.unreachableCase)(kind);
            }
        }
    });
}
// TODO: Use a stronger type for leaf schemas once one is available (see object schema handler for an example).
function leafSchemaToSimpleSchema(schema) {
    return {
        kind: index_js_2.NodeKind.Leaf,
        leafKind: schema.info,
    };
}
// TODO: Use a stronger type for array schemas once one is available (see object schema handler for an example).
function arraySchemaToSimpleSchema(schema) {
    const fieldSchema = (0, schemaTypes_js_1.normalizeFieldSchema)(schema.info);
    const allowedTypes = allowedTypesFromFieldSchema(fieldSchema);
    const output = {
        kind: index_js_2.NodeKind.Array,
        allowedTypes,
    };
    (0, index_js_1.copyProperty)(schema, "metadata", output);
    return output;
}
// TODO: Use a stronger type for map schemas once one is available (see object schema handler for an example).
function mapSchemaToSimpleSchema(schema) {
    const fieldSchema = (0, schemaTypes_js_1.normalizeFieldSchema)(schema.info);
    const allowedTypes = allowedTypesFromFieldSchema(fieldSchema);
    const output = {
        kind: index_js_2.NodeKind.Map,
        allowedTypes,
    };
    (0, index_js_1.copyProperty)(schema, "metadata", output);
    return output;
}
function objectSchemaToSimpleSchema(schema) {
    const fields = {};
    for (const [key, field] of schema.fields) {
        fields[key] = fieldSchemaToSimpleSchema(field);
    }
    const output = {
        kind: index_js_2.NodeKind.Object,
        fields,
    };
    (0, index_js_1.copyProperty)(schema, "metadata", output);
    return output;
}
/**
 * Private symbol under which the results of {@link toSimpleNodeSchema} are cached on an input {@link TreeNodeSchema}.
 */
const simpleFieldSchemaCacheSymbol = Symbol("simpleFieldSchemaCache");
function fieldSchemaToSimpleSchema(schema) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    if (schema[simpleFieldSchemaCacheSymbol] !== undefined) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return schema[simpleFieldSchemaCacheSymbol];
    }
    const allowedTypes = allowedTypesFromFieldSchema(schema);
    const result = {
        kind: schema.kind,
        allowedTypes,
    };
    (0, index_js_1.copyProperty)(schema, "metadata", result);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    schema[simpleFieldSchemaCacheSymbol] = result;
    return result;
}
function allowedTypesFromFieldSchema(schema) {
    const allowedTypes = new Set();
    for (const type of schema.allowedTypeSet) {
        allowedTypes.add(type.identifier);
    }
    return allowedTypes;
}
/**
 * Recursively populates `definitions` by walking the input field schema tree.
 */
function populateSchemaDefinitionsForField(schema, definitions) {
    for (const child of schema.allowedTypeSet) {
        populateSchemaDefinitionsForNode(child, definitions);
    }
}
/**
 * Recursively populates `definitions` by walking the input node schema tree.
 */
function populateSchemaDefinitionsForNode(schema, definitions) {
    if (definitions.has(schema.identifier)) {
        // If the definition has already been populated, no need to recurse.
        return;
    }
    // Populate definition for this schema
    definitions.set(schema.identifier, toSimpleNodeSchema(schema));
    // Recurse into children to populate definitions for them
    const kind = schema.kind;
    switch (kind) {
        case index_js_2.NodeKind.Leaf: {
            // Leaf node, so no need to recurse
            break;
        }
        case index_js_2.NodeKind.Map: {
            // TODO: Utilize a map schema type-guard once one exists (see object case for an example).
            // Recursively populate definitions for allowed map children
            const fieldSchema = (0, schemaTypes_js_1.normalizeFieldSchema)(schema.info);
            populateSchemaDefinitionsForField(fieldSchema, definitions);
            break;
        }
        case index_js_2.NodeKind.Array: {
            // TODO: Utilize an array schema type-guard once one exists (see object case for an example).
            // Recursively populate definitions for allowed map children
            const fieldSchema = (0, schemaTypes_js_1.normalizeFieldSchema)(schema.info);
            populateSchemaDefinitionsForField(fieldSchema, definitions);
            break;
        }
        case index_js_2.NodeKind.Object: {
            (0, internal_1.assert)((0, objectNodeTypes_js_1.isObjectNodeSchema)(schema), 0xa07 /* Expected object schema */);
            for (const [, field] of schema.fields) {
                populateSchemaDefinitionsForField(field, definitions);
            }
            break;
        }
        default: {
            (0, internal_1.unreachableCase)(kind);
        }
    }
}
//# sourceMappingURL=viewSchemaToSimpleSchema.js.map