"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.RawMapNode = exports.mapSchema = void 0;
const proxies_js_1 = require("./proxies.js");
const proxyBinding_js_1 = require("./proxyBinding.js");
const schemaCaching_js_1 = require("./schemaCaching.js");
const schemaTypes_js_1 = require("./schemaTypes.js");
const toMapTree_js_1 = require("./toMapTree.js");
const types_js_1 = require("./types.js");
const toFlexSchema_js_1 = require("./toFlexSchema.js");
const rawNode_js_1 = require("./rawNode.js");
const handler = {
    getPrototypeOf: () => {
        return Map.prototype;
    },
};
class CustomMapNodeBase extends types_js_1.TreeNodeValid {
    [Symbol.iterator]() {
        return this.entries();
    }
    delete(key) {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        node.delete(key);
    }
    *entries() {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        for (const key of node.keys()) {
            yield [
                key,
                (0, proxies_js_1.getProxyForField)(node.getBoxed(key)),
            ];
        }
    }
    get(key) {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        const field = node.getBoxed(key);
        return (0, proxies_js_1.getProxyForField)(field);
    }
    has(key) {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        return node.has(key);
    }
    keys() {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        return node.keys();
    }
    set(key, value) {
        const node = (0, proxyBinding_js_1.getFlexNode)(this);
        const content = (0, proxies_js_1.prepareContentForInsert)(value, node.context.checkout.forest);
        const classSchema = (0, schemaCaching_js_1.getSimpleNodeSchema)(node.schema);
        const cursor = (0, toMapTree_js_1.cursorFromNodeData)(content, classSchema.info, node.context.nodeKeyManager);
        node.set(key, cursor);
        return this;
    }
    get size() {
        return (0, proxyBinding_js_1.getFlexNode)(this).size;
    }
    *values() {
        for (const [, value] of this.entries()) {
            yield value;
        }
    }
    forEach(callbackFn, thisArg) {
        for (const field of (0, proxyBinding_js_1.getFlexNode)(this).boxedIterator()) {
            const node = (0, proxies_js_1.getProxyForField)(field);
            callbackFn.call(thisArg, node, field.key, this);
        }
    }
}
CustomMapNodeBase.kind = schemaTypes_js_1.NodeKind.Map;
/**
 * Define a {@link TreeNodeSchema} for a {@link (TreeArrayNode:interface)}.
 *
 * @param base - base schema type to extend.
 * @param useMapPrototype - should this type emulate a ES6 Map object (by faking its prototype with a proxy).
 */
function mapSchema(identifier, info, implicitlyConstructable, useMapPrototype) {
    let flexSchema;
    class schema extends CustomMapNodeBase {
        static prepareInstance(instance, flexNode) {
            if (useMapPrototype) {
                return new Proxy(instance, handler);
            }
            return instance;
        }
        static buildRawNode(instance, input) {
            return new RawMapNode(flexSchema, copyContent(flexSchema.name, input));
        }
        static oneTimeSetup() {
            flexSchema = (0, toFlexSchema_js_1.getFlexSchema)(this);
        }
        get [schemaTypes_js_1.type]() {
            return identifier;
        }
    }
    schema.constructorCached = undefined;
    schema.identifier = identifier;
    schema.info = info;
    schema.implicitlyConstructable = implicitlyConstructable;
    const schemaErased = schema;
    return schemaErased;
}
exports.mapSchema = mapSchema;
/**
 * The implementation of a map node created by {@link createRawNode}.
 */
class RawMapNode extends rawNode_js_1.RawTreeNode {
    get size() {
        return this[rawNode_js_1.nodeContent].size;
    }
    has(key) {
        return this[rawNode_js_1.nodeContent].has(key);
    }
    get(key) {
        return this[rawNode_js_1.nodeContent].get(key);
    }
    keys() {
        return this[rawNode_js_1.nodeContent].keys();
    }
    values() {
        throw (0, rawNode_js_1.rawError)("Iterating map values");
    }
    entries() {
        throw (0, rawNode_js_1.rawError)("Iterating map entries");
    }
    forEach(callbackFn, thisArg) {
        throw (0, rawNode_js_1.rawError)("Iterating maps with forEach");
    }
    set(key, value) {
        throw (0, rawNode_js_1.rawError)("Setting a map entry");
    }
    delete(key) {
        throw (0, rawNode_js_1.rawError)("Deleting a map entry");
    }
    get asObject() {
        throw (0, rawNode_js_1.rawError)("Converting a map to an object");
    }
    [Symbol.iterator]() {
        return this.entries();
    }
    boxedIterator() {
        throw (0, rawNode_js_1.rawError)("Boxed iteration");
    }
}
exports.RawMapNode = RawMapNode;
function copyContent(typeName, content) {
    const copy = new Map(content);
    (0, proxies_js_1.markContentType)(typeName, copy);
    return copy;
}
//# sourceMappingURL=mapNode.js.map