"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.emptyTypeAttributes = exports.TypeAttributeKind = void 0;
exports.combineTypeAttributes = combineTypeAttributes;
exports.makeTypeAttributesInferred = makeTypeAttributesInferred;
exports.increaseTypeAttributesDistance = increaseTypeAttributesDistance;
const collection_utils_1 = require("collection-utils");
const Support_js_1 = require("../support/Support.js");
class TypeAttributeKind {
    constructor(name) {
        this.name = name;
    }
    appliesToTypeKind(kind) {
        return kind !== "any";
    }
    combine(_attrs) {
        return (0, Support_js_1.panic)(`Cannot combine type attribute ${this.name}`);
    }
    intersect(attrs) {
        return this.combine(attrs);
    }
    makeInferred(_) {
        return (0, Support_js_1.panic)(`Cannot make type attribute ${this.name} inferred`);
    }
    increaseDistance(attrs) {
        return attrs;
    }
    addToSchema(_schema, _t, _attrs) {
        return;
    }
    children(_) {
        return new Set();
    }
    stringify(_) {
        return undefined;
    }
    get inIdentity() {
        return false;
    }
    requiresUniqueIdentity(_) {
        return false;
    }
    reconstitute(_builder, a) {
        return a;
    }
    makeAttributes(value) {
        const kvps = [[this, value]];
        return new Map(kvps);
    }
    tryGetInAttributes(a) {
        return a.get(this);
    }
    setInAttributes(a, value) {
        // FIXME: This is potentially super slow
        return new Map(a).set(this, value);
    }
    modifyInAttributes(a, modify) {
        const modified = modify(this.tryGetInAttributes(a));
        if (modified === undefined) {
            // FIXME: This is potentially super slow
            const result = new Map(a);
            result.delete(this);
            return result;
        }
        return this.setInAttributes(a, modified);
    }
    setDefaultInAttributes(a, makeDefault) {
        if (this.tryGetInAttributes(a) !== undefined)
            return a;
        return this.modifyInAttributes(a, makeDefault);
    }
    removeInAttributes(a) {
        return (0, collection_utils_1.mapFilter)(a, (_, k) => k !== this);
    }
    equals(other) {
        if (!(other instanceof TypeAttributeKind)) {
            return false;
        }
        return this.name === other.name;
    }
    hashCode() {
        return (0, collection_utils_1.hashString)(this.name);
    }
}
exports.TypeAttributeKind = TypeAttributeKind;
exports.emptyTypeAttributes = new Map();
function combineTypeAttributes(combinationKind, firstOrArray, second) {
    const union = combinationKind === "union";
    let attributeArray;
    if (Array.isArray(firstOrArray)) {
        attributeArray = firstOrArray;
    }
    else {
        if (second === undefined) {
            return (0, Support_js_1.panic)("Must have on array or two attributes");
        }
        attributeArray = [firstOrArray, second];
    }
    const attributesByKind = (0, collection_utils_1.mapTranspose)(attributeArray);
    // FIXME: strongly type this
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    function combine(attrs, kind) {
        (0, Support_js_1.assert)(attrs.length > 0, "Cannot combine zero type attributes");
        if (attrs.length === 1)
            return attrs[0];
        if (union) {
            return kind.combine(attrs);
        }
        else {
            return kind.intersect(attrs);
        }
    }
    return (0, collection_utils_1.mapFilterMap)(attributesByKind, combine);
}
function makeTypeAttributesInferred(attr) {
    return (0, collection_utils_1.mapFilterMap)(attr, (value, kind) => kind.makeInferred(value));
}
function increaseTypeAttributesDistance(attr) {
    return (0, collection_utils_1.mapFilterMap)(attr, (value, kind) => kind.increaseDistance(value));
}
