"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPlugPlugged = exports.isPlugPluggedTo = exports.isPinMerged = exports.isPinInMerge = void 0;
exports.getSpec = getSpec;
exports.getGraphSpec = getGraphSpec;
exports.getSpecs = getSpecs;
exports.hasSpec = hasSpec;
exports.getComponentSpec = getComponentSpec;
exports.getSpecPins = getSpecPins;
exports.getSpecIdPinPlugs = getSpecIdPinPlugs;
exports.getSpecPinPlugs = getSpecPinPlugs;
exports.getSpecInputs = getSpecInputs;
exports.getSpecOutputs = getSpecOutputs;
exports.getSpecIdPinPlugCount = getSpecIdPinPlugCount;
exports.getSpecPinPlugCount = getSpecPinPlugCount;
exports.getSpecPinCount = getSpecPinCount;
exports.getSpecInputCount = getSpecInputCount;
exports.getSpecOutputCount = getSpecOutputCount;
exports.emptySpec = emptySpec;
exports.emptyShell = emptyShell;
exports.getSpecName = getSpecName;
exports.getSpecTitle = getSpecTitle;
exports.newSpecId = newSpecId;
exports.newUnitIdInSpecId = newUnitIdInSpecId;
exports.newUnitId = newUnitId;
exports.newUnitIdFromName = newUnitIdFromName;
exports.newPinId = newPinId;
exports.newMergeId = newMergeId;
exports.hasUnitId = hasUnitId;
exports.hasMergeId = hasMergeId;
exports.sameSpec = sameSpec;
exports.sameUnits = sameUnits;
exports.sameMerges = sameMerges;
exports.sameInputs = sameInputs;
exports.sameOutputs = sameOutputs;
exports.samePins = samePins;
exports.samePin = samePin;
exports.sameMetadata = sameMetadata;
exports.isInternalSpecId = isInternalSpecId;
exports.isSystemSpecId = isSystemSpecId;
exports.isSystemSpec = isSystemSpec;
exports.isEmptySpec = isEmptySpec;
exports.getSpecRenderById = getSpecRenderById;
exports.getSpecRender = getSpecRender;
exports.isComponentId = isComponentId;
exports.isComponentSpec = isComponentSpec;
exports.hasSubComponent = hasSubComponent;
exports.hasSubComponents = hasSubComponents;
exports.shouldRender = shouldRender;
exports.findInputDataExamples = findInputDataExamples;
exports.getSingleMergePin = getSingleMergePin;
exports.remapUnitMerges = remapUnitMerges;
exports.remapUnitPlugs = remapUnitPlugs;
exports.removeBundleMetadata = removeBundleMetadata;
exports.removeBundleMemory = removeBundleMemory;
exports.removeSpecMemory = removeSpecMemory;
exports.removeSpecMetadata = removeSpecMetadata;
const STRING_1 = require("../constant/STRING");
const deepSet_1 = require("../deepSet");
const f_1 = __importDefault(require("../system/core/object/ForEachKeyValue/f"));
const f_2 = require("../system/core/object/KeyCount/f");
const f_3 = __importDefault(require("../system/f/comparison/Equals/f"));
const f_4 = __importDefault(require("../system/f/object/DeepMerge/f"));
const f_5 = require("../system/f/object/Keys/f");
const IOOf_1 = require("../types/IOOf");
const clone_1 = require("../util/clone");
const id_1 = require("../util/id");
const object_1 = require("../util/object");
const string_1 = require("../util/string");
const emptySpec_1 = require("./emptySpec");
const SpecNotFoundError_1 = require("./SpecNotFoundError");
const component_1 = require("./util/component");
const spec_1 = require("./util/spec");
function getSpec(specs, id) {
    const spec = specs[id];
    if (!spec) {
        throw new SpecNotFoundError_1.SpecNotFoundError();
    }
    return spec;
}
function getGraphSpec(specs, id) {
    const spec = specs[id];
    return spec;
}
function getSpecs(specs) {
    return specs;
}
function hasSpec(specs, id) {
    const spec = specs[id];
    return !!spec;
}
function getComponentSpec(specs, id) {
    const spec = getSpec(specs, id);
    const component = (0, component_1.getSpecComponentSpec)(spec);
    return component;
}
function getSpecPins(specs, id, type) {
    const spec = getSpec(specs, id);
    const pins = spec[`${type}s`] || {};
    return pins;
}
function getSpecIdPinPlugs(specs, id, type, pinId) {
    return (0, object_1.deepGetOrDefault)(specs, [id, `${type}s`, pinId, 'plug'], {});
}
function getSpecPinPlugs(spec, type, pinId) {
    return (0, object_1.deepGetOrDefault)(spec, [`${type}s`, pinId, 'plug'], {});
}
function getSpecInputs(specs, id) {
    const spec = getSpec(specs, id);
    const { inputs = {} } = spec;
    return inputs;
}
function getSpecOutputs(specs, id) {
    const spec = getSpec(specs, id);
    const { outputs = {} } = spec;
    return outputs;
}
function getSpecIdPinPlugCount(specs, id, type, pinId) {
    const plugs = getSpecIdPinPlugs(specs, id, type, pinId);
    const count = (0, f_5.keys)(plugs || {}).length;
    return count;
}
function getSpecPinPlugCount(spec, type, pinId) {
    const plugs = getSpecPinPlugs(spec, type, pinId);
    const count = (0, f_5.keys)(plugs || {}).length;
    return count;
}
function getSpecPinCount(specs, id, type) {
    const pins = getSpecPins(specs, id, type);
    const inputCount = (0, f_5.keys)(pins || {}).length;
    return inputCount;
}
function getSpecInputCount(specs, id) {
    const inputs = getSpecInputs(specs, id);
    const inputCount = (0, f_5.keys)(inputs || {}).length;
    return inputCount;
}
function getSpecOutputCount(specs, id) {
    const outputs = getSpecOutputs(specs, id);
    const outputCount = (0, f_5.keys)(outputs || {}).length;
    return outputCount;
}
function emptySpec(init = {}) {
    const newSpec = (0, clone_1.clone)({
        ...(0, emptySpec_1.emptyGraphSpec)(),
        ...init,
    });
    return newSpec;
}
function emptyShell(from, init = {}) {
    const newSpec = (0, clone_1.clone)({
        ...(0, emptySpec_1.emptyGraphSpec)(),
        ...init,
    });
    return newSpec;
}
function getSpecName(specs, id) {
    const spec = getSpec(specs, id);
    const { name } = spec;
    return name;
}
function getSpecTitle(specs, id) {
    return getSpecName(specs, id);
}
function newSpecId(specs) {
    const specId = (0, id_1.uuidNotIn)(specs);
    return specId;
}
function newUnitIdInSpecId(specs, id, unit_spec_id, blacklist) {
    const spec = getSpec(specs, id);
    return newUnitId(specs, spec, unit_spec_id, blacklist);
}
function newUnitId(specs, spec, unit_spec_id, blacklist = new Set()) {
    const unit_spec = getSpec(specs, unit_spec_id);
    const { name = STRING_1.UNTITLED } = unit_spec;
    return newUnitIdFromName(spec, name, blacklist);
}
function newUnitIdFromName(spec, name, blacklist = new Set()) {
    const init_new_unit_id = (0, string_1.removeWhiteSpace)(name);
    let new_unit_id = init_new_unit_id;
    let i = 0;
    while (hasUnitId(spec, new_unit_id) || blacklist.has(new_unit_id)) {
        new_unit_id = init_new_unit_id + i;
        i++;
    }
    return new_unit_id;
}
function newPinId(spec, type, start, blacklist = new Set()) {
    let newPinId = start;
    let i = 0;
    while ((0, spec_1.hasPin)(spec, type, newPinId) || blacklist.has(newPinId)) {
        newPinId = `${start}${i}`;
        i++;
    }
    return newPinId;
}
function newMergeId(spec, blacklist = new Set()) {
    let i = 0;
    let new_merge_id = `${i}`;
    while (hasMergeId(spec, new_merge_id) || blacklist.has(new_merge_id)) {
        i++;
        new_merge_id = `${i}`;
    }
    return new_merge_id;
}
function hasUnitId(spec, unitId) {
    const { units = {} } = spec;
    const has = !!units[unitId];
    return has;
}
function hasMergeId(spec, mergeId) {
    const { merges = {} } = spec;
    const has = !!merges[mergeId];
    return has;
}
const isPinInMerge = (spec, mergeId, unitId, type, pinId) => {
    return !!(spec.merges[mergeId] &&
        spec.merges[mergeId][unitId] &&
        spec.merges[mergeId][unitId][type] &&
        spec.merges[mergeId][unitId][type][pinId]);
};
exports.isPinInMerge = isPinInMerge;
const isPinMerged = (spec, unitId, type, pinId) => {
    for (const mergeId in spec.merges ?? {}) {
        if ((0, exports.isPinInMerge)(spec, mergeId, unitId, type, pinId)) {
            return true;
        }
    }
    return false;
};
exports.isPinMerged = isPinMerged;
const isPlugPluggedTo = (spec, type, pinId, subPinId, subPinSpec) => {
    const subPinSpec_ = (0, spec_1.getSubPinSpec)(spec, type, pinId, subPinId);
    if (subPinSpec_.unitId === subPinSpec.unitId &&
        (subPinSpec_.kind ?? type) === (subPinSpec.kind ?? type) &&
        subPinSpec_.pinId === subPinSpec.pinId &&
        subPinSpec_.mergeId === subPinSpec.mergeId) {
        return true;
    }
    return false;
};
exports.isPlugPluggedTo = isPlugPluggedTo;
const isPlugPlugged = (spec, type, pinId, subPinId) => {
    const subPinSpec_ = (0, spec_1.getSubPinSpec)(spec, type, pinId, subPinId);
    if (subPinSpec_.unitId || subPinSpec_.mergeId) {
        return true;
    }
    return false;
};
exports.isPlugPlugged = isPlugPlugged;
function sameSpec(a, b) {
    a = (0, f_4.default)(emptySpec(), a);
    b = (0, f_4.default)(emptySpec(), b);
    if (a.name !== b.name) {
        return false;
    }
    if (!sameUnits(a, b)) {
        return false;
    }
    if (!sameMerges(a, b)) {
        return false;
    }
    if (!sameInputs(a, b)) {
        return false;
    }
    if (!sameOutputs(a, b)) {
        return false;
    }
    // if (!sameMetadata(a, b)) {
    //   return false
    // }
    return true;
}
function sameUnits(a, b) {
    if ((0, f_2.keyCount)(a.units) !== (0, f_2.keyCount)(b.units)) {
        return false;
    }
    for (const unitId in a.units) {
        if (!b.units[unitId]) {
            return false;
        }
        a.units[unitId] = (0, f_4.default)({ input: {} }, a.units[unitId]);
        b.units[unitId] = (0, f_4.default)({ input: {} }, b.units[unitId]);
        for (const inputId in a.units[unitId].input) {
            const aInput = a.units[unitId].input[inputId] ?? {};
            const bInput = b.units[unitId].input[inputId] ?? {};
            if (!!aInput.constant !== !!bInput.constant) {
                return false;
            }
            if (!(0, f_3.default)(aInput.data, bInput.data)) {
                return false;
            }
        }
    }
    return true;
}
function sameMerges(a, b) {
    for (const mergeId in a.merges) {
        if (!b.merges[mergeId]) {
            return false;
        }
        const aMergePinCount = (0, spec_1.getMergePinCount)(a.merges[mergeId]);
        const bMergePinCount = (0, spec_1.getMergePinCount)(b.merges[mergeId]);
        if (aMergePinCount !== bMergePinCount) {
            return false;
        }
        const merge = a.merges[mergeId];
        for (const unitId in merge) {
            if (!b.merges[mergeId][unitId]) {
                return false;
            }
            for (const type in merge[unitId]) {
                for (const pinId in merge[unitId][type]) {
                    if (!b.merges[mergeId]?.[unitId]?.[type]?.[pinId]) {
                        return false;
                    }
                }
            }
        }
    }
    return true;
}
function sameInputs(a, b) {
    return samePins(a, b, 'input');
}
function sameOutputs(a, b) {
    return samePins(a, b, 'output');
}
function samePins(a, b, type) {
    for (const pinId in a[`${type}s`]) {
        let aPin = a[`${type}s`][pinId] ?? {};
        let bPin = b[`${type}s`][pinId] ?? {};
        if (!bPin) {
            return false;
        }
        if (!samePin(aPin, bPin)) {
            return false;
        }
    }
    return true;
}
function samePin(a, b) {
    a = (0, f_4.default)({ plug: {} }, a);
    b = (0, f_4.default)({ plug: {} }, b);
    const { plug } = a;
    for (const subPinId in plug) {
        const aSubPin = a.plug[subPinId];
        const bSubPin = b.plug[subPinId];
        if (!bSubPin) {
            return false;
        }
        if (!(0, f_3.default)(aSubPin, bSubPin)) {
            return false;
        }
    }
    return true;
}
function sameMetadata(a, b) {
    let aMetadata = a.metadata ?? {};
    let bMetadata = b.metadata ?? {};
    aMetadata = (0, f_4.default)({ description: '', icon: null }, aMetadata);
    bMetadata = (0, f_4.default)({ description: '', icon: null }, bMetadata);
    if (!(0, f_3.default)(aMetadata, bMetadata)) {
        return false;
    }
    return true;
}
function isInternalSpecId(specId) {
    return specId.startsWith('_');
}
function isSystemSpecId(specs, specId) {
    if (isInternalSpecId(specId)) {
        return true;
    }
    if (!hasSpec(specs, specId)) {
        return false;
    }
    const spec = getSpec(specs, specId);
    const { system } = spec;
    return system;
}
function isSystemSpec(spec) {
    const { system } = spec;
    return system;
}
function isEmptySpec(spec) {
    const { units = {}, base } = spec;
    if (base) {
        return false;
    }
    const unitCount = (0, f_5.keys)(units).length;
    const empty = unitCount === 0;
    return empty;
}
function getSpecRenderById(specs, id) {
    const spec = getSpec(specs, id);
    return getSpecRender(spec);
}
function getSpecRender(spec) {
    const { render } = spec;
    return render;
}
function isComponentId(specs, id) {
    return (getSpecRenderById(specs, id) ?? hasSubComponents(specs[id]));
}
function isComponentSpec(spec) {
    return getSpecRender(spec) ?? hasSubComponents(spec);
}
function hasSubComponent(spec, subComponentId) {
    const { component = {} } = spec;
    const { subComponents = {} } = component;
    return !!subComponents[subComponentId];
}
function hasSubComponents(spec) {
    const { component = {} } = spec;
    const { subComponents = {} } = component;
    return (0, f_2.keyCount)(subComponents) > 0;
}
function shouldRender(componentSpec) {
    return !!componentSpec.children && componentSpec.children.length > 0;
}
function findInputDataExamples(specs, spec_id, input_id) {
    const spec = getSpec(specs, spec_id);
    const { units, merges, inputs, base } = spec;
    const input = inputs[input_id];
    const { metadata = {} } = input;
    const { examples = [] } = metadata;
    if (examples.length > 0) {
        return examples;
    }
    else {
        if (base) {
            return [];
        }
        else {
            const { plug } = input;
            for (const subPinId in plug) {
                const subPin = plug[subPinId];
                const { unitId, kind = 'input', pinId, mergeId } = subPin;
                if (kind !== 'input') {
                    continue;
                }
                if (unitId && pinId) {
                    const unit = units[unitId];
                    const unit_examples = findInputDataExamples(specs, unit.id, pinId);
                    if (unit_examples.length > 0) {
                        return unit_examples;
                    }
                }
                else if (mergeId) {
                    const merge = merges[mergeId];
                    for (const unitId in merge) {
                        const mergeUnit = merge[unitId];
                        const { input = {} } = mergeUnit;
                        const unit = units[unitId];
                        for (const inputId in input) {
                            const unit_examples = findInputDataExamples(specs, unit.id, inputId);
                            if (unit_examples.length > 0) {
                                return unit_examples;
                            }
                        }
                    }
                }
            }
        }
    }
    return [];
}
function getSingleMergePin(merge) {
    const unitId = (0, object_1.getObjSingleKey)(merge);
    const unitMerge = merge[unitId];
    const type = (0, object_1.getObjSingleKey)(unitMerge);
    const typePins = unitMerge[type];
    const pinId = (0, object_1.getObjSingleKey)(typePins);
    return { unitId, type, pinId };
}
function remapUnitMerges(merges, unitMap, unitLinkMap) {
    const merges_ = {};
    (0, spec_1.forEachPinOnMerges)(merges, (mergeId, unitId, type, pinId) => {
        const nextUnitId = (0, object_1.deepGetOrDefault)(unitMap, [unitId], unitId);
        const nextPinId = (0, object_1.deepGetOrDefault)(unitLinkMap, [unitId, type, pinId], pinId);
        (0, deepSet_1.deepSet_)(merges_, [mergeId, nextUnitId, type, nextPinId], true);
    });
    return merges_;
}
function remapUnitPlugs(plugs, linkMap) {
    const plugs_ = {};
    (0, IOOf_1.forIOObjKV)(plugs, (type, pinId, plug) => {
        const nextPinId = (0, object_1.deepGetOrDefault)(linkMap, [type, pinId], pinId);
        (0, deepSet_1.deepSet_)(plugs_, [type, nextPinId], plug);
    });
    return plugs_;
}
function removeBundleMetadata(bundle) {
    const { spec = {}, specs = {} } = bundle;
    removeSpecMetadata(spec);
    (0, f_1.default)(specs, removeSpecMetadata);
}
function removeBundleMemory(bundle) {
    const { spec = {}, specs = {} } = bundle;
    removeSpecMemory(spec);
    (0, f_1.default)(specs, removeSpecMemory);
}
function removeSpecMemory(spec) {
    const { units = {} } = spec;
    for (const unitId in units) {
        const unit = units[unitId];
        delete unit.memory;
    }
}
function removeSpecMetadata(spec) {
    const { units = {}, inputs = {}, outputs = {}, metadata = {}, data = {}, } = spec;
    for (const unitId in units) {
        const unit = units[unitId];
        delete unit.metadata;
        (0, f_1.default)(unit.input ?? {}, (input) => {
            delete input.metadata;
        });
        (0, f_1.default)(unit.output ?? {}, (output) => {
            delete output.metadata;
        });
    }
    (0, f_1.default)(inputs, (input) => {
        delete input.metadata;
        delete input.icon;
    });
    (0, f_1.default)(outputs, (output) => {
        delete output.metadata;
        delete output.icon;
    });
    for (const datumId in data) {
        const datum = data[datumId];
        delete datum.metadata;
    }
    delete metadata.icon;
    delete metadata.position;
}
//# sourceMappingURL=util.js.map