/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { isFluidHandle } from "@fluidframework/runtime-utils/internal";
import { assert } from "@fluidframework/core-utils/internal";
import { EmptyKey, forEachField, inCursorField, LeafNodeStoredSchema, mapCursorField, ObjectNodeStoredSchema, } from "../../core/index.js";
import { fail } from "../../util/index.js";
import { NodeKind } from "../core/index.js";
import { booleanSchema, handleSchema, nullSchema, numberSchema, stringSchema, } from "../leafNodeSchema.js";
import { isObjectNodeSchema } from "../objectNodeTypes.js";
import { FieldKinds, valueSchemaAllows } from "../../feature-libraries/index.js";
/**
 * Builds an {@link CustomTree} from a cursor in Nodes mode.
 */
export function customFromCursor(reader, options, schema, childHandler) {
    const type = reader.type;
    const nodeSchema = schema.get(type) ?? fail(0xb2e /* missing schema for type in cursor */);
    switch (type) {
        case numberSchema.identifier:
        case booleanSchema.identifier:
        case nullSchema.identifier:
        case stringSchema.identifier:
            assert(reader.value !== undefined, 0xa50 /* out of schema: missing value */);
            assert(!isFluidHandle(reader.value), 0xa51 /* out of schema: unexpected FluidHandle */);
            return reader.value;
        case handleSchema.identifier:
            assert(reader.value !== undefined, 0xa52 /* out of schema: missing value */);
            assert(isFluidHandle(reader.value), 0xa53 /* out of schema: expected FluidHandle */);
            return options.valueConverter(reader.value);
        default: {
            assert(reader.value === undefined, 0xa54 /* out of schema: unexpected value */);
            if (nodeSchema.kind === NodeKind.Array) {
                const fields = inCursorField(reader, EmptyKey, () => mapCursorField(reader, () => childHandler(reader, options, schema)));
                return fields;
            }
            else {
                const fields = {};
                forEachField(reader, () => {
                    const children = mapCursorField(reader, () => childHandler(reader, options, schema));
                    if (children.length === 1) {
                        const storedKey = reader.getFieldKey();
                        const key = isObjectNodeSchema(nodeSchema) && !options.useStoredKeys
                            ? (nodeSchema.storedKeyToPropertyKey.get(storedKey) ??
                                fail(0xb2f /* missing property key */))
                            : storedKey;
                        // Length is checked above.
                        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                        fields[key] = children[0];
                    }
                    else {
                        assert(children.length === 0, 0xa19 /* invalid children number */);
                    }
                });
                return fields;
            }
        }
    }
}
/**
 * Builds an {@link CustomTree} from a cursor in Nodes mode.
 * @remarks
 * Uses stored keys and stored schema.
 */
export function customFromCursorStored(reader, schema, childHandler) {
    const type = reader.type;
    const nodeSchema = schema.get(type) ?? fail(0xb30 /* missing schema for type in cursor */);
    if (nodeSchema instanceof LeafNodeStoredSchema) {
        assert(valueSchemaAllows(nodeSchema.leafValue, reader.value), 0xa9c /* invalid value */);
        return reader.value;
    }
    assert(reader.value === undefined, 0xa9d /* out of schema: unexpected value */);
    const arrayTypes = tryStoredSchemaAsArray(nodeSchema);
    if (arrayTypes !== undefined) {
        const field = inCursorField(reader, EmptyKey, () => mapCursorField(reader, () => childHandler(reader, schema)));
        return field;
    }
    const fields = {};
    forEachField(reader, () => {
        const children = mapCursorField(reader, () => childHandler(reader, schema));
        if (children.length === 1) {
            const storedKey = reader.getFieldKey();
            // Length is checked above.
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            fields[storedKey] = children[0];
        }
        else {
            assert(children.length === 0, 0xa9e /* invalid children number */);
        }
    });
    return fields;
}
/**
 * Assumes `schema` corresponds to a simple-tree schema.
 * If it is an array schema, returns the allowed types for the array field.
 * Otherwise returns `undefined`.
 */
export function tryStoredSchemaAsArray(schema) {
    if (schema instanceof ObjectNodeStoredSchema) {
        const empty = schema.getFieldSchema(EmptyKey);
        if (empty.kind === FieldKinds.sequence.identifier) {
            assert(schema.objectNodeFields.size === 1, 0xa9f /* invalid schema */);
            return empty.types;
        }
    }
}
//# sourceMappingURL=customTree.js.map