/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { strict as assert, fail } from "assert";
// eslint-disable-next-line import/no-internal-modules
import { handleShapesAndIdentifiers } from "../../../../feature-libraries/chunked-forest/codec/chunkEncodingGeneric.js";
import { version,
// eslint-disable-next-line import/no-internal-modules
 } from "../../../../feature-libraries/chunked-forest/codec/format.js";
import { assertChunkCursorBatchEquals } from "../fieldCursorTestUtilities.js";
import { cursorForJsonableTreeNode, cursorForJsonableTreeField, isFluidHandle, } from "../../../../feature-libraries/index.js";
// eslint-disable-next-line import/no-internal-modules
import { decode } from "../../../../feature-libraries/chunked-forest/codec/chunkDecoding.js";
export function checkNodeEncode(shape, cache, tree) {
    const buffer = [shape.shape];
    const cursor = cursorForJsonableTreeNode(tree);
    shape.encodeNode(cursor, cache, buffer);
    // Check round-trip
    checkDecode([buffer], [[tree]]);
    return buffer.slice(1);
}
export function checkFieldEncode(shape, cache, tree) {
    const buffer = [shape.shape];
    const cursor = cursorForJsonableTreeField(tree);
    shape.encodeField(cursor, cache, buffer);
    // Check round-trip
    checkDecode([buffer], [tree]);
    return buffer.slice(1);
}
function checkDecode(buffer, tree) {
    // Check round-trips with identifiers inline and out of line
    testDecode(buffer, tree, () => false);
    testDecode(buffer, tree, () => true);
}
/**
 * Clones anything handleShapesAndIdentifiers might modify in-place.
 */
function cloneArrays(data) {
    return data.map((item) => (Array.isArray(item) ? cloneArrays(item) : item));
}
function testDecode(buffer, expectedTree, identifierFilter) {
    const chunk = handleShapesAndIdentifiers(version, cloneArrays(buffer), identifierFilter);
    // TODO: check chunk matches schema
    // Check decode
    const result = decode(chunk);
    assertChunkCursorBatchEquals(result, expectedTree);
    // handles can't be roundtripped through JSON. the FluidSerializer can't be
    // used to roundtrip handles in this case either, as doing so changes the
    // contents of the handle compared to the original object. avoid the below
    // roundtripping in that case.
    function hasHandle(data) {
        if (Array.isArray(data)) {
            return data.some(hasHandle);
        }
        return isFluidHandle(data);
    }
    if (hasHandle(chunk.data)) {
        return chunk;
    }
    // Confirm JSON compatibility
    {
        assertJsonish(chunk, new Set());
        const json = JSON.stringify(chunk);
        const parsed = JSON.parse(json);
        // can't check this due to undefined fields
        // assert.deepEqual(parsed, chunk);
        // Instead check that it works properly:
        const parsedResult = decode(parsed);
        assert.deepEqual(parsedResult, result);
    }
    return chunk;
}
/**
 * Checks that data is json compatible.
 *
 * This assumes that `undefined` fields are allowed as JSON will omit them.
 * If tolerating undefined fields is not desired, a similar check can be done by round-tripping through json instead.
 */
function assertJsonish(data, stack) {
    switch (typeof data) {
        case "number":
            assert(Number.isFinite(data));
            assert(!Object.is(data, -0));
            return;
        case "string":
        // TODO: could test that string is valid unicode here.
        case "boolean":
            return;
        case "object": {
            if (data === null) {
                return;
            }
            assert(!stack.has(data));
            stack.add(data);
            try {
                if (Array.isArray(data)) {
                    for (const item of data) {
                        assertJsonish(item, stack);
                    }
                }
                const prototype = Reflect.getPrototypeOf(data);
                if (prototype !== Object && prototype === null) {
                    fail();
                }
                for (const key of Reflect.ownKeys(data)) {
                    assert(typeof key === "string");
                    const value = Reflect.get(data, key);
                    if (value !== undefined) {
                        // TODO: could check for feature detection pattern, used for IFluidHandle
                        assertJsonish(value, stack);
                    }
                }
                return;
            }
            finally {
                stack.delete(data);
            }
        }
        default:
            fail();
    }
}
//# sourceMappingURL=checkEncode.js.map