"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setMetadata = exports.removeUnitPinData = exports.setUnitInputIgnored = exports.setUnitOutputIgnored = exports.setUnitPinIgnored = exports.setUnitOutputConstant = exports.setUnitInputConstant = exports.setUnitPinConstant = exports.setUnitPinData = exports.setUnitOutput = exports.setUnitInput = exports.setPinSetRef = exports.setPinSetFunctional = exports.setPinSetId = exports.setPinSetName = exports.setUnitErr = exports.coverOutput = exports.coverOutputSet = exports.exposeOutput = exports.exposeOutputSet = exports.coverInput = exports.coverInputSet = exports.unplugPin = exports.unplugOutput = exports.unplugInput = exports.plugPin = exports.exposeInput = exports.exposeInputSet = exports.exposePin = exports.setUnitId = exports.exposePinSet = exports.coverPin = exports.coverPinSet = exports.isPinMerged = exports.removePinFromMerge = exports._removePinFromMerge = exports.mergeMerges = exports.removeMerge = exports.addMerges = exports.addPinToMerge = exports.addMerge = exports.removeUnit = exports.removeUnitMerges = exports.removeUnitExposedPins = exports.removeUnitExposedTypePins = exports.mergeUnits = exports.removeUnits = exports.addUnits = exports.addUnit = exports.setName = void 0;
exports.setUnitMetadata = void 0;
const f_1 = require("../../system/core/object/AssocPath/f");
const f_2 = require("../../system/core/object/DeepGet/f");
const f_3 = require("../../system/core/object/DissocPath/f");
const f_4 = require("../../system/core/object/ForEachKeyValue/f");
const f_5 = require("../../system/f/object/DeepMerge/f");
const f_6 = require("../../system/f/object/Dissoc/f");
const f_7 = require("../../system/f/object/Merge/f");
const f_8 = require("../../system/f/object/Set/f");
const array_1 = require("../../util/array");
const object_1 = require("../../util/object");
const util_1 = require("../util");
const setName = ({ name }, spec) => {
    (0, object_1.pathSet)(spec, ['name'], name);
};
exports.setName = setName;
const addUnit = ({ unitId, unit }, spec) => {
    (0, object_1.pathSet)(spec, ['units', unitId], unit);
};
exports.addUnit = addUnit;
const addUnits = ({ units }, spec) => {
    (0, f_5._deepMerge)(spec.units, units);
};
exports.addUnits = addUnits;
const removeUnits = ({ ids }, spec) => {
    (0, array_1.forEach)(ids, (id) => {
        (0, exports.removeUnit)({ unitId: id }, spec);
    });
};
exports.removeUnits = removeUnits;
const mergeUnits = ({ units }, spec) => {
    (0, f_8.default)(spec, 'units', (0, f_7.default)(spec.units, units));
};
exports.mergeUnits = mergeUnits;
const removeUnitExposedTypePins = ({ id, type }, spec) => {
    for (const pinId in spec[`${type}s`]) {
        const pin = spec[`${type}s`][pinId];
        const { plug } = pin;
        for (let subPinId in plug) {
            const subPin = plug[subPinId];
            const { unitId } = subPin;
            if (unitId === id) {
                (0, object_1.pathSet)(spec, [`${type}s`, pinId, 'plug', subPinId], {});
            }
        }
    }
};
exports.removeUnitExposedTypePins = removeUnitExposedTypePins;
const removeUnitExposedPins = ({ unitId: id }, spec) => {
    (0, exports.removeUnitExposedTypePins)({ id, type: 'input' }, spec);
    (0, exports.removeUnitExposedTypePins)({ id, type: 'output' }, spec);
};
exports.removeUnitExposedPins = removeUnitExposedPins;
const removeUnitMerges = ({ unitId: id }, spec) => {
    const { merges = {} } = spec;
    let nextState = (0, object_1.clone)(spec);
    const removedMergeIds = {};
    (0, f_4.default)(merges, (merge, mergeId) => {
        if (merge[id]) {
            delete nextState.merges[mergeId][id];
            if ((0, util_1.getMergePinCount)(nextState.merges[mergeId]) < 2) {
                delete nextState.merges[mergeId];
                removedMergeIds[mergeId] = true;
            }
        }
    });
    const reasignExposedPins = (type) => {
        const pins = spec[`${type}s`] || {};
        for (const exposedPinId in pins) {
            const pinSpec = pins[exposedPinId];
            const { plug } = pinSpec;
            for (let subPinId in plug) {
                const subPin = plug[subPinId];
                const { mergeId } = subPin;
                if (mergeId && removedMergeIds[mergeId]) {
                    for (const unitId in merges[mergeId]) {
                        if (unitId !== id) {
                            const pinId = (0, object_1.getObjSingleKey)((0, object_1.pathOrDefault)(merges, [mergeId, unitId, type], {}));
                            if (pinId) {
                                nextState[`${type}s`][exposedPinId]['plug'][subPinId] = {
                                    unitId,
                                    pinId,
                                };
                                break;
                            }
                            else {
                                nextState[`${type}s`][exposedPinId]['plug'][subPinId] = {};
                                break;
                            }
                        }
                    }
                }
            }
        }
    };
    reasignExposedPins('input');
    reasignExposedPins('output');
};
exports.removeUnitMerges = removeUnitMerges;
const removeUnit = ({ unitId }, spec) => {
    (0, exports.removeUnitMerges)({ unitId }, spec);
    (0, exports.removeUnitExposedPins)({ unitId }, spec);
    delete spec['units'][unitId];
};
exports.removeUnit = removeUnit;
const addMerge = ({ mergeId, merge }, spec) => {
    const { inputs = {}, outputs = {} } = spec;
    (0, f_4.default)(inputs, ({ plug }, inputId) => {
        (0, f_4.default)(plug, ({ unitId, pinId }, subPinId) => {
            var _a, _b;
            if (unitId && pinId && ((_b = (_a = merge[unitId]) === null || _a === void 0 ? void 0 : _a['input']) === null || _b === void 0 ? void 0 : _b[pinId])) {
                (0, exports.coverInput)({ id: inputId, subPinId }, spec);
                (0, exports.exposeInput)({ id: inputId, subPinId, input: { mergeId } }, spec);
            }
        });
    });
    // reassign exposed outputs that have just been merged
    (0, f_4.default)(outputs, ({ plug }, outputId) => {
        (0, f_4.default)(plug, ({ unitId, pinId }, subPinId) => {
            if (unitId &&
                pinId &&
                merge[unitId] &&
                merge[unitId]['output'] &&
                merge[unitId]['output'][pinId]) {
                (0, exports.coverOutput)({ id: outputId, subPinId }, spec);
                (0, exports.exposeOutput)({ id: outputId, subPinId, output: { mergeId } }, spec);
            }
        });
    });
    return (0, f_1.default)(spec, ['merges', mergeId], merge);
};
exports.addMerge = addMerge;
const addPinToMerge = ({ id, unitId, type, pinId, }, spec) => {
    // reassign exposed pin if it has just been merged
    (0, f_4.default)(spec[`${type}s`], ({ name, plug = {} }, exposedPinId) => {
        for (const subPinId in plug) {
            const subPin = plug[subPinId];
            const { unitId: _unitId, pinId: _pinId } = subPin;
            if (_unitId === unitId && _pinId === pinId) {
                (0, exports.coverPinSet)({ pinId: exposedPinId, type }, spec);
                (0, exports.exposePinSet)({
                    pinId: exposedPinId,
                    pinSpec: { name, plug: { 0: { mergeId: id } } },
                    type,
                }, spec);
            }
        }
    });
    return (0, f_1.default)(spec, ['merges', id, unitId, type, pinId], true);
};
exports.addPinToMerge = addPinToMerge;
const addMerges = ({ merges }, spec) => {
    // TODO update exposed inputs/outputs
    (0, object_1.pathSet)(spec, ['merges'], (0, f_5.default)(merges, spec.merges));
};
exports.addMerges = addMerges;
const removeMerge = ({ id }, spec) => {
    const nextState = (0, object_1.clone)(spec);
    const nextMerges = (0, f_6.default)(spec.merges, id);
    nextState.merges = nextMerges;
    nextState.inputs = (0, object_1.mapObjVK)(spec.inputs, (input) => (Object.assign(Object.assign({}, input), { plug: (0, object_1.mapObjVK)(input.plug, (plug) => {
            const { mergeId } = plug;
            return !mergeId || mergeId !== id ? plug : {};
        }) })));
    nextState.outputs = (0, object_1.mapObjVK)(spec.outputs, (output) => (Object.assign(Object.assign({}, output), { plug: (0, object_1.mapObjVK)(output.plug, (plug) => {
            const { mergeId } = plug;
            return !mergeId || mergeId !== id ? plug : {};
        }) })));
    if (nextState.metadata &&
        nextState.metadata.position &&
        nextState.metadata.position.merge) {
        delete nextState.metadata.position.merge[id];
    }
};
exports.removeMerge = removeMerge;
const mergeMerges = ({ a, b }, spec) => {
    const aMerge = spec.merges[a];
    const bMerge = spec.merges[b];
    (0, object_1.pathDelete)(spec, ['merges', b]);
    (0, object_1.pathSet)(spec, ['merges', a], (0, f_5.default)(aMerge, bMerge));
};
exports.mergeMerges = mergeMerges;
const _removePinFromMerge = ({ id, unitId, type, pinId, }, spec) => {
    (0, object_1.pathDelete)(spec, ['merges', id, unitId, type, pinId]);
    if ((0, object_1.isEmptyObject)((0, f_2.default)(spec, ['merges', id, unitId, type]))) {
        (0, object_1.pathDelete)(spec, ['merges', id, unitId, type]);
    }
    if ((0, object_1.isEmptyObject)((0, f_2.default)(spec, ['merges', id, unitId]))) {
        (0, object_1.pathDelete)(spec, ['merges', id, unitId]);
    }
};
exports._removePinFromMerge = _removePinFromMerge;
const removePinFromMerge = ({ id, unitId, type, pinId, }, spec) => {
    (0, exports._removePinFromMerge)({ id, unitId, type, pinId }, spec);
};
exports.removePinFromMerge = removePinFromMerge;
const isPinMerged = (spec, id, unitId, type, pinId) => {
    return !!(spec.merges[id] &&
        spec.merges[id][unitId] &&
        spec.merges[id][unitId][type] &&
        spec.merges[id][unitId][type][pinId]);
};
exports.isPinMerged = isPinMerged;
const coverPinSet = ({ pinId, type }, spec) => (0, object_1.pathDelete)(spec, [`${type}s`, pinId]);
exports.coverPinSet = coverPinSet;
const coverPin = ({ pinId, type, subPinId, }, spec) => {
    (0, object_1.pathDelete)(spec, [`${type}s`, pinId, 'plug', subPinId]);
};
exports.coverPin = coverPin;
const exposePinSet = ({ pinId, type, pinSpec }, spec) => (0, object_1.pathSet)(spec, [`${type}s`, pinId], pinSpec);
exports.exposePinSet = exposePinSet;
const setUnitId = ({ unitId, newUnitId, name, }, spec) => {
    const unit = spec.units[unitId];
    (0, exports.removeUnit)({ unitId }, spec);
    (0, exports.addUnit)({ unitId: newUnitId, unit }, spec);
};
exports.setUnitId = setUnitId;
const exposePin = ({ pinId, type, subPinId, subPinSpec, }, spec) => {
    return (0, object_1.pathSet)(spec, [`${type}s`, pinId, 'plug', subPinId], subPinSpec);
};
exports.exposePin = exposePin;
const exposeInputSet = ({ id, input }, spec) => (0, exports.exposePinSet)({ pinId: id, pinSpec: input, type: 'input' }, spec);
exports.exposeInputSet = exposeInputSet;
const exposeInput = ({ id, subPinId, input, }, spec) => {
    return (0, exports.exposePin)({ pinId: id, subPinId, subPinSpec: input, type: 'input' }, spec);
};
exports.exposeInput = exposeInput;
const plugPin = ({ type, id, subPinId, subPinSpec, }, spec) => {
    return (0, f_1.default)(spec, [`${type}s`, id, 'plug', subPinId], subPinSpec);
};
exports.plugPin = plugPin;
const unplugInput = ({ id, subPinId, }, spec) => {
    return (0, exports.unplugPin)({ type: 'input', pinId: id, subPinId }, spec);
};
exports.unplugInput = unplugInput;
const unplugOutput = ({ id, subPinId, }, spec) => {
    return (0, exports.unplugPin)({ type: 'output', pinId: id, subPinId }, spec);
};
exports.unplugOutput = unplugOutput;
const unplugPin = ({ type, pinId, subPinId, }, spec) => {
    return (0, f_1.default)(spec, [`${type}s`, pinId, 'plug', subPinId], {});
};
exports.unplugPin = unplugPin;
const coverInputSet = ({ id }, spec) => (0, exports.coverPinSet)({ pinId: id, type: 'input' }, spec);
exports.coverInputSet = coverInputSet;
const coverInput = ({ id, subPinId, }, spec) => {
    return (0, exports.coverPin)({ pinId: id, type: 'input', subPinId }, spec);
};
exports.coverInput = coverInput;
const exposeOutputSet = ({ id, output }, spec) => (0, exports.exposePinSet)({ pinId: id, pinSpec: output, type: 'output' }, spec);
exports.exposeOutputSet = exposeOutputSet;
const exposeOutput = ({ id, subPinId, output, }, spec) => {
    return (0, exports.exposePin)({ pinId: id, subPinId, subPinSpec: output, type: 'output' }, spec);
};
exports.exposeOutput = exposeOutput;
const coverOutputSet = ({ id }, spec) => (0, exports.coverPinSet)({ pinId: id, type: 'output' }, spec);
exports.coverOutputSet = coverOutputSet;
const coverOutput = ({ id, subPinId, }, spec) => {
    return (0, exports.coverPin)({ pinId: id, type: 'output', subPinId }, spec);
};
exports.coverOutput = coverOutput;
const setUnitErr = ({ unitId, err }, spec) => {
    if (err !== null) {
        return (0, f_1.default)(spec, ['units', unitId, 'err'], err);
    }
    else {
        return (0, object_1.pathDelete)(spec, ['units', unitId, 'err']);
    }
};
exports.setUnitErr = setUnitErr;
const setPinSetName = ({ type, id, name }, spec) => {
    return (0, f_1.default)(spec, [`${type}s`, id, 'name'], name);
};
exports.setPinSetName = setPinSetName;
const setPinSetId = ({ type, id, newId }, spec) => {
    return (0, f_1.default)((0, f_3.default)(spec, [`${type}s`, id]), [`${type}s`, newId], (0, f_2.default)(spec, [`${type}s`, id]));
};
exports.setPinSetId = setPinSetId;
const setPinSetFunctional = ({ type, pinId, functional }, spec) => {
    return (0, f_1.default)(spec, [`${type}s`, pinId, 'functional'], functional);
};
exports.setPinSetFunctional = setPinSetFunctional;
const setPinSetRef = ({ type, pinId, ref }, spec) => {
    return (0, f_1.default)(spec, [`${type}s`, pinId, 'ref'], ref);
};
exports.setPinSetRef = setPinSetRef;
const setUnitInput = ({ id, name, data }, spec) => {
    return (0, f_1.default)(spec, ['units', id, 'input', name], data);
};
exports.setUnitInput = setUnitInput;
const setUnitOutput = ({ id, name, data }, spec) => {
    return (0, f_1.default)(spec, ['units', id, 'output', name], data);
};
exports.setUnitOutput = setUnitOutput;
const setUnitPinData = ({ unitId, type, pinId, data, }, spec) => {
    return (0, object_1.pathSet)(spec, ['units', unitId, type, pinId, 'data'], data);
};
exports.setUnitPinData = setUnitPinData;
const setUnitPinConstant = ({ unitId, type, pinId, constant, }, spec) => {
    return (0, object_1.pathSet)(spec, ['units', unitId, type, pinId, 'constant'], constant);
};
exports.setUnitPinConstant = setUnitPinConstant;
const setUnitInputConstant = ({ unitId, pinId, constant, }, spec) => {
    return (0, f_1.default)(spec, ['units', unitId, 'input', pinId, 'constant'], constant);
};
exports.setUnitInputConstant = setUnitInputConstant;
const setUnitOutputConstant = ({ unitId, pinId, constant, }, spec) => {
    return (0, f_1.default)(spec, ['units', unitId, 'output', pinId, 'constant'], constant);
};
exports.setUnitOutputConstant = setUnitOutputConstant;
const setUnitPinIgnored = ({ unitId, type, pinId, ignored, }, spec) => {
    if (ignored) {
        (0, util_1.forEachPinOnMerges)(spec.merges, (id, _unitId, _type, _pinId) => {
            if (_unitId === unitId && _type === type && _pinId === pinId) {
                (0, exports.removePinFromMerge)({ id, unitId, type, pinId }, spec);
            }
        });
        if (type === 'input') {
            (0, f_4.default)(spec.inputs, ({ plug }, id) => {
                (0, f_4.default)(plug, (input, subPinId) => {
                    if (input.unitId === unitId && input.pinId === pinId) {
                        (0, exports.unplugInput)({ id, subPinId }, spec);
                    }
                });
            });
        }
        if (type === 'output') {
            (0, f_4.default)(spec.outputs, ({ plug }, id) => {
                (0, f_4.default)(plug, (output, subPinId) => {
                    if (output.unitId === unitId && output.pinId === pinId) {
                        (0, exports.unplugOutput)({ id, subPinId }, spec);
                    }
                });
            });
        }
    }
    spec = (0, f_1.default)(spec, ['units', unitId, type, pinId, 'ignored'], ignored);
    return spec;
};
exports.setUnitPinIgnored = setUnitPinIgnored;
const setUnitOutputIgnored = ({ unitId, pinId, ignored, }, spec) => {
    (0, exports.setUnitPinIgnored)({ unitId, type: 'output', pinId, ignored }, spec);
};
exports.setUnitOutputIgnored = setUnitOutputIgnored;
const setUnitInputIgnored = ({ unitId, pinId, ignored, }, spec) => {
    (0, exports.setUnitPinIgnored)({ unitId, type: 'input', pinId, ignored }, spec);
};
exports.setUnitInputIgnored = setUnitInputIgnored;
const removeUnitPinData = ({ unitId, type, pinId }, spec) => {
    (0, object_1.pathDelete)(spec, ['units', unitId, type, pinId, 'data']);
};
exports.removeUnitPinData = removeUnitPinData;
const setMetadata = ({ path, value }, spec) => {
    (0, object_1.pathSet)(spec, path, value);
};
exports.setMetadata = setMetadata;
const setUnitMetadata = ({ id, path, value }, spec) => {
    (0, object_1.pathSet)(spec, ['units', id, 'metadata', ...path], value);
};
exports.setUnitMetadata = setUnitMetadata;
//# sourceMappingURL=spec_.js.map