"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renameUnitInMerges = exports.renameUnitPin = exports.setSubComponentSize = exports.setComponentSize = exports.setUnitSize = exports.setUnitMetadata = exports.setMetadata = exports.removeUnitPinData = exports.setUnitInputIgnored = exports.setUnitOutputIgnored = exports.setUnitPinIgnored = exports.setPinSetDefaultIgnored = exports.setUnitPinConstant = exports.setUnitPinData = exports.setUnitOutput = exports.setUnitInput = exports.setPinSetRef = exports.setPinSetDataType = exports.setPinSetFunctional = exports.setPinSetId = exports.coverOutput = exports.coverOutputSet = exports.exposeOutput = exports.exposeOutputSet = exports.coverInput = exports.coverInputSet = exports.unplugPin = exports.unplugOutput = exports.unplugInput = exports.plugPin = exports.exposePin = exports.setUnitId = exports.exposePinSet = exports.coverPin = exports.coverPinSet = exports.removePinFromMerge = exports._removePinFromMerge = exports.mergeMerges = exports.removeMerge = exports.addPinToMerge = exports.addMerge = exports.removeUnit = exports.removeUnitMerges = exports.removeUnitExposedPins = exports.removeUnitExposedTypePins = exports.setDatum = exports.removeDatum = exports.addUnitSpec = exports.addUnit = exports.setName = void 0;
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/object/DeepMerge/f"));
const IOOf_1 = require("../../types/IOOf");
const array_1 = require("../../util/array");
const clone_1 = require("../../util/clone");
const object_1 = require("../../util/object");
const evaluateDataValue_1 = require("../evaluateDataValue");
const component_1 = require("../util/component");
const spec_1 = require("../util/spec");
const component_2 = require("./component");
const setName = ({ name }, spec) => {
    (0, deepSet_1.deepSet_)(spec, ['name'], name);
};
exports.setName = setName;
const addUnit = ({ unitId, unit }, spec) => {
    (0, deepSet_1.deepSet_)(spec, ['units', unitId], unit);
};
exports.addUnit = addUnit;
const addUnitSpec = ({ unitId, bundle }, spec) => {
    (0, deepSet_1.deepSet_)(spec, ['units', unitId], bundle.unit);
};
exports.addUnitSpec = addUnitSpec;
const removeDatum = ({ datumId }, spec) => {
    (0, object_1.deepDelete)(spec, ['data', datumId]);
};
exports.removeDatum = removeDatum;
const setDatum = ({ datumId, value }, spec) => {
    (0, deepSet_1.deepSet_)(spec, ['data', datumId], value);
};
exports.setDatum = setDatum;
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, kind = type } = subPin;
            if (unitId === id) {
                (0, deepSet_1.deepSet_)(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 }, spec) => {
    const { merges = {} } = (0, clone_1.clone)(spec);
    const removedMergeIdSet = new Set();
    (0, f_1.default)(merges, (merge, mergeId) => {
        if (merge[unitId]) {
            (0, object_1.deepDelete)(spec, ['merges', mergeId, unitId]);
            if ((0, spec_1.getMergePinCount)(spec.merges[mergeId]) < 2) {
                (0, object_1.deepDestroy)(spec, ['merges', mergeId]);
                removedMergeIdSet.add(mergeId);
            }
        }
    });
    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 && removedMergeIdSet.has(mergeId)) {
                    const mergeSpec = (0, clone_1.clone)(merges[mergeId]);
                    delete mergeSpec[unitId];
                    const mergePinCount = (0, spec_1.getMergePinCount)(mergeSpec);
                    if (mergePinCount > 1) {
                        continue;
                    }
                    else if (mergePinCount === 1) {
                        const otherMergeUnitId = (0, object_1.getObjSingleKey)(mergeSpec);
                        const otherMergeUnitType = (0, object_1.getObjSingleKey)(mergeSpec?.[otherMergeUnitId] ?? {});
                        const otherMergeUnitPinId = (0, object_1.getObjSingleKey)(mergeSpec?.[otherMergeUnitId]?.[otherMergeUnitType] ?? {});
                        (0, deepSet_1.deepSet_)(spec, [`${type}s`, exposedPinId, 'plug', subPinId], {
                            unitId: otherMergeUnitId,
                            pinId: otherMergeUnitPinId,
                            kind: otherMergeUnitType,
                        });
                    }
                    else {
                        //
                    }
                }
            }
        }
    };
    reasignExposedPins('input');
    reasignExposedPins('output');
};
exports.removeUnitMerges = removeUnitMerges;
const removeUnit = ({ unitId }, spec) => {
    (0, exports.removeUnitMerges)({ unitId }, spec);
    (0, exports.removeUnitExposedPins)({ unitId }, spec);
    (0, object_1.deepDestroy)(spec, ['units', unitId]);
};
exports.removeUnit = removeUnit;
const addMerge = ({ mergeId, mergeSpec }, spec) => {
    (0, IOOf_1.io)((type) => {
        const pins = spec[`${type}s`] || {};
        (0, f_1.default)(pins, ({ plug }, pinId) => {
            (0, f_1.default)(plug, (subPinSpec, subPinId) => {
                if (subPinSpec.unitId &&
                    subPinSpec.pinId &&
                    (0, object_1.deepGetOrDefault)(mergeSpec, [subPinSpec.unitId, subPinSpec.kind ?? type, subPinSpec.pinId], null)) {
                    (0, exports.coverPin)({ type, pinId, subPinId }, spec);
                    (0, exports.exposePin)({ type, pinId, subPinId, subPinSpec: { mergeId } }, spec);
                }
            });
        });
    });
    return (0, deepSet_1.deepSet_)(spec, ['merges', mergeId], mergeSpec);
};
exports.addMerge = addMerge;
const addPinToMerge = ({ mergeId, unitId, type, pinId, }, spec) => {
    (0, IOOf_1.io)((type_) => {
        (0, f_1.default)(spec[`${type_}s`], ({ plug = {} }, exposedPinId) => {
            for (const subPinId in plug) {
                const subPin = plug[subPinId];
                const { unitId: _unitId, pinId: _pinId, kind = type_ } = subPin;
                if (_unitId === unitId && _pinId === pinId && kind === type) {
                    (0, exports.unplugPin)({ pinId: exposedPinId, type: kind, subPinId }, spec);
                    (0, exports.plugPin)({
                        type: type_,
                        pinId: exposedPinId,
                        subPinId,
                        subPinSpec: { mergeId },
                    }, spec);
                }
            }
        });
    });
    return (0, deepSet_1.deepSet_)(spec, ['merges', mergeId, unitId, type, pinId], true);
};
exports.addPinToMerge = addPinToMerge;
const removeMerge = ({ mergeId }, spec) => {
    if (!spec.merges || !spec.merges[mergeId]) {
        return;
    }
    delete spec.merges[mergeId];
    spec.inputs = (0, object_1.mapObjVK)(spec.inputs, (input) => ({
        ...input,
        plug: (0, object_1.mapObjVK)(input.plug, (plug) => {
            return !plug.mergeId || mergeId !== plug.mergeId ? plug : {};
        }),
    }));
    if (!(0, f_2.keyCount)(spec.inputs)) {
        delete spec.inputs;
    }
    spec.outputs = (0, object_1.mapObjVK)(spec.outputs, (output) => ({
        ...output,
        plug: (0, object_1.mapObjVK)(output.plug, (plug) => {
            return !plug.mergeId || mergeId !== plug.mergeId ? plug : {};
        }),
    }));
    if (!(0, f_2.keyCount)(spec.outputs)) {
        delete spec.outputs;
    }
    if (spec.metadata && spec.metadata.position && spec.metadata.position.merge) {
        delete spec.metadata.position.merge[mergeId];
    }
    if (!(0, f_2.keyCount)(spec.merges ?? {})) {
        delete spec.merges;
    }
};
exports.removeMerge = removeMerge;
const mergeMerges = ({ a, b }, spec) => {
    const aMerge = spec.merges[a];
    const bMerge = spec.merges[b];
    (0, object_1.deepDelete)(spec, ['merges', b]);
    (0, deepSet_1.deepSet_)(spec, ['merges', a], (0, f_3.default)(aMerge, bMerge));
};
exports.mergeMerges = mergeMerges;
const _removePinFromMerge = ({ mergeId, unitId, type, pinId, }, spec) => {
    (0, object_1.deepDelete)(spec, ['merges', mergeId, unitId, type, pinId]);
    if ((0, object_1.isEmptyObject)((0, object_1.deepGetOrDefault)(spec, ['merges', mergeId, unitId, type], {}))) {
        (0, object_1.deepDelete)(spec, ['merges', mergeId, unitId, type]);
    }
    if ((0, object_1.isEmptyObject)((0, object_1.deepGetOrDefault)(spec, ['merges', mergeId, unitId], {}))) {
        (0, object_1.deepDelete)(spec, ['merges', mergeId, unitId]);
    }
};
exports._removePinFromMerge = _removePinFromMerge;
const removePinFromMerge = ({ mergeId, unitId, type, pinId, }, spec) => {
    (0, exports._removePinFromMerge)({ mergeId, unitId, type, pinId }, spec);
};
exports.removePinFromMerge = removePinFromMerge;
const coverPinSet = ({ pinId, type }, spec) => {
    const pinSpec = (0, spec_1.getPinSpec)(spec, type, pinId);
    const { plug = {} } = pinSpec;
    const mergesToCheck = new Set();
    for (const subPinId in plug) {
        const subPinSpec = plug[subPinId];
        if (subPinSpec.mergeId) {
            mergesToCheck.add(subPinSpec.mergeId);
        }
    }
    (0, object_1.deepDestroy)(spec, [`${type}s`, pinId]);
    for (const mergeId of mergesToCheck) {
        const merge = (0, spec_1.getMerge)(spec, mergeId);
        if ((0, spec_1.getMergePinCount)(merge) === 0) {
            (0, exports.removeMerge)({ mergeId }, spec);
        }
    }
};
exports.coverPinSet = coverPinSet;
const coverPin = ({ type, pinId, subPinId, }, spec) => {
    (0, object_1.deepDelete)(spec, [`${type}s`, pinId, 'plug', subPinId]);
};
exports.coverPin = coverPin;
const exposePinSet = ({ type, pinId, pinSpec }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId], pinSpec);
};
exports.exposePinSet = exposePinSet;
const setUnitId = ({ unitId, newUnitId }, spec) => {
    const unit = spec.units[unitId];
    const subComponent = (0, object_1.deepGetOrDefault)(spec, ['component', 'subComponents', unitId], undefined);
    const parentId = (subComponent && (0, component_1.getSubComponentParentId)(spec, unitId)) || null;
    const specClone = (0, clone_1.clone)(spec);
    const unitPlugs = (0, spec_1.findUnitPlugs)(specClone, unitId);
    const unitMerges = (0, spec_1.findUnitMerges)(specClone, unitId);
    (0, exports.removeUnit)({ unitId }, spec);
    (0, exports.addUnit)({ unitId: newUnitId, unit }, spec);
    if (subComponent) {
        (0, component_2.removeSubComponent)({ unitId }, spec.component);
        (0, component_2.setSubComponent)({ unitId: newUnitId, subComponent }, spec.component);
        if (parentId) {
            const at = spec.component.subComponents[parentId].children.indexOf(unitId);
            const slotName = spec.component.subComponents?.[parentId]?.childSlot?.[unitId] ??
                'default';
            (0, component_2.removeSubComponentChild)({ parentId: parentId, childId: unitId }, spec.component);
            (0, component_2.insertSubComponentChild)({ parentId, childId: newUnitId, slotName, at }, spec.component);
        }
        else {
            const at = spec.component.children.indexOf(unitId);
            (0, component_2.removeRoot)({ childId: unitId }, spec.component);
            (0, component_2.insertRoot)({ childId: newUnitId, at }, spec.component);
        }
    }
    for (const mergeId in unitMerges) {
        const mergeSpec = unitMerges[mergeId];
        for (const _unitId in mergeSpec) {
            if (_unitId === unitId) {
                mergeSpec[newUnitId] = mergeSpec[_unitId];
                delete mergeSpec[_unitId];
            }
        }
        (0, exports.addMerge)({ mergeId, mergeSpec }, spec);
    }
    (0, IOOf_1.forIOObjKV)(unitPlugs, (type, pinId, plugs) => {
        (0, array_1.forEach)(plugs, ({ type: _type, pinId: _pinId, subPinId: _subPinId }) => {
            const subPinSpec = (0, object_1.deepGet)(specClone, [
                `${_type ?? type}s`,
                _pinId,
                'plug',
                _subPinId,
            ]);
            if (subPinSpec.unitId === unitId) {
                subPinSpec.unitId = newUnitId;
            }
            (0, deepSet_1.deepSet_)(spec, [`${_type ?? type}s`, _pinId, 'plug', _subPinId], subPinSpec);
        });
    });
};
exports.setUnitId = setUnitId;
const exposePin = ({ pinId, type, subPinId, subPinSpec, pinSpec }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'plug', subPinId], subPinSpec);
};
exports.exposePin = exposePin;
const plugPin = ({ type, pinId, subPinId, subPinSpec, }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'plug', subPinId], subPinSpec);
};
exports.plugPin = plugPin;
const unplugInput = ({ pinId, subPinId, }, spec) => {
    return (0, exports.unplugPin)({ type: 'input', pinId, subPinId }, spec);
};
exports.unplugInput = unplugInput;
const unplugOutput = ({ pinId, subPinId, }, spec) => {
    return (0, exports.unplugPin)({ type: 'output', pinId, subPinId }, spec);
};
exports.unplugOutput = unplugOutput;
const unplugPin = ({ type, pinId, subPinId, }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'plug', subPinId], {});
};
exports.unplugPin = unplugPin;
const coverInputSet = ({ pinId }, spec) => (0, exports.coverPinSet)({ pinId, type: 'input' }, spec);
exports.coverInputSet = coverInputSet;
const coverInput = ({ pinId, subPinId, }, spec) => {
    return (0, exports.coverPin)({ pinId, type: 'input', subPinId }, spec);
};
exports.coverInput = coverInput;
const exposeOutputSet = ({ pinId, output }, spec) => (0, exports.exposePinSet)({ pinId, pinSpec: output, type: 'output' }, spec);
exports.exposeOutputSet = exposeOutputSet;
const exposeOutput = ({ pinId, subPinId, output, }, spec) => {
    return (0, exports.exposePin)({ pinId, subPinId, subPinSpec: output, type: 'output' }, spec);
};
exports.exposeOutput = exposeOutput;
const coverOutputSet = ({ pinId }, spec) => (0, exports.coverPinSet)({ pinId, type: 'output' }, spec);
exports.coverOutputSet = coverOutputSet;
const coverOutput = ({ pinId, subPinId, }, spec) => {
    return (0, exports.coverPin)({ pinId, type: 'output', subPinId }, spec);
};
exports.coverOutput = coverOutput;
const setPinSetId = ({ type, pinId, newPinId }, spec) => {
    const pinSpec = (0, object_1.deepGet)(spec, [`${type}s`, pinId]);
    (0, object_1.deepDelete)(spec, [`${type}s`, pinId]);
    (0, deepSet_1.deepSet_)(spec, [`${type}s`, newPinId], pinSpec);
};
exports.setPinSetId = setPinSetId;
const setPinSetFunctional = ({ type, pinId, functional }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'functional'], functional);
};
exports.setPinSetFunctional = setPinSetFunctional;
const setPinSetDataType = ({ type, pinId, dataType }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'type'], dataType);
};
exports.setPinSetDataType = setPinSetDataType;
const setPinSetRef = ({ type, pinId, ref }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'ref'], ref);
};
exports.setPinSetRef = setPinSetRef;
const setUnitInput = ({ id, name, data }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, ['units', id, 'input', name], data);
};
exports.setUnitInput = setUnitInput;
const setUnitOutput = ({ id, name, data }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, ['units', id, 'output', name], data);
};
exports.setUnitOutput = setUnitOutput;
const setUnitPinData = ({ unitId, type, pinId, data, }, spec, specs, classes) => {
    const dataRef = (0, evaluateDataValue_1.evaluateDataValue)(data, specs, classes);
    return (0, deepSet_1.deepSet_)(spec, ['units', unitId, type, pinId, 'data'], dataRef);
};
exports.setUnitPinData = setUnitPinData;
const setUnitPinConstant = ({ unitId, type, pinId, constant, }, spec) => {
    return (0, deepSet_1.deepSet_)(spec, ['units', unitId, type, pinId, 'constant'], constant);
};
exports.setUnitPinConstant = setUnitPinConstant;
const setPinSetDefaultIgnored = ({ type, pinId, defaultIgnored, }, spec) => {
    (0, deepSet_1.deepSet_)(spec, [`${type}s`, pinId, 'defaultIgnored'], defaultIgnored);
};
exports.setPinSetDefaultIgnored = setPinSetDefaultIgnored;
const setUnitPinIgnored = ({ unitId, type, pinId, ignored, }, spec) => {
    if (ignored) {
        (0, spec_1.forEachPinOnMerges)(spec.merges ?? {}, (mergeId, _unitId, _type, _pinId) => {
            if (_unitId === unitId && _type === type && _pinId === pinId) {
                (0, exports.removePinFromMerge)({ mergeId, unitId, type, pinId }, spec);
            }
        });
        if (type === 'input') {
            (0, f_1.default)(spec.inputs ?? {}, ({ plug }, _pinId) => {
                (0, f_1.default)(plug, (input, subPinId) => {
                    if (input.unitId === unitId && input.pinId === pinId) {
                        (0, exports.unplugInput)({ pinId: _pinId, subPinId }, spec);
                    }
                });
            });
        }
        if (type === 'output') {
            (0, f_1.default)(spec.outputs ?? {}, ({ plug }, _pinId) => {
                (0, f_1.default)(plug, (output, subPinId) => {
                    if (output.unitId === unitId && output.pinId === pinId) {
                        (0, exports.unplugOutput)({ pinId: _pinId, subPinId }, spec);
                    }
                });
            });
        }
    }
    (0, deepSet_1.deepSet_)(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.deepDelete)(spec, ['units', unitId, type, pinId, 'data']);
};
exports.removeUnitPinData = removeUnitPinData;
const setMetadata = ({ path, value }, spec) => {
    (0, deepSet_1.deepSet_)(spec, path, value);
};
exports.setMetadata = setMetadata;
const setUnitMetadata = ({ id, path, value }, spec) => {
    (0, deepSet_1.deepSet_)(spec, ['units', id, 'metadata', ...path], value);
};
exports.setUnitMetadata = setUnitMetadata;
const setUnitSize = ({ unitId, width, height }, spec) => {
    (0, object_1.deepDeepMerge)(spec, ['units', unitId, 'metadata', 'component'], {
        width,
        height,
    });
};
exports.setUnitSize = setUnitSize;
const setComponentSize = ({ width, height }, spec) => {
    (0, object_1.deepDeepMerge)(spec, ['component'], {
        defaultWidth: width,
        defaultHeight: height,
    });
};
exports.setComponentSize = setComponentSize;
const setSubComponentSize = ({ unitId, width, height }, spec) => {
    (0, object_1.deepDeepMerge)(spec, ['component', 'subComponents', unitId], {
        width,
        height,
    });
};
exports.setSubComponentSize = setSubComponentSize;
const renameUnitPin = ({ unitId, type, pinId, newPinId, }, spec) => {
    const { units = {}, merges = {} } = spec;
    const unit = units[unitId];
    const pin = unit[type]?.[pinId];
    if (pin) {
        delete unit[type]?.[pinId];
        (0, deepSet_1.deepSet_)(unit, [type, newPinId], pin);
    }
    (0, spec_1.forEachPinOnMerges)((0, clone_1.clone)(merges), (mergeId, _unitId, _type, _pinId) => {
        if (_unitId === unitId && _type === type && _pinId === pinId) {
            (0, object_1.deepDelete)(spec, ['merges', mergeId, unitId, type, pinId]);
            (0, deepSet_1.deepSet_)(spec, ['merges', mergeId, unitId, type, newPinId], true);
        }
    });
    (0, spec_1.forEachGraphSpecPinPlug)((0, clone_1.clone)(spec), (type_, pinId_, pinSpec, subPinId, subPinSpec) => {
        if (subPinSpec.unitId === unitId &&
            subPinSpec.pinId === pinId &&
            (subPinSpec.kind ?? type_) === type) {
            (0, deepSet_1.deepSet_)(spec, [`${type_}s`, pinId_, 'plug', subPinId, 'pinId'], newPinId);
        }
    });
};
exports.renameUnitPin = renameUnitPin;
const renameUnitInMerges = (unitId, unitMerges, newUnitId) => {
    (0, f_1.default)((0, clone_1.clone)(unitMerges), (merge, mergeId) => {
        (0, f_1.default)(merge, (pins, mergeUnitId) => {
            if (unitId === mergeUnitId) {
                delete unitMerges[mergeId][unitId];
                unitMerges[mergeId][newUnitId] = pins;
            }
        });
    });
};
exports.renameUnitInMerges = renameUnitInMerges;
//# sourceMappingURL=spec.js.map