"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renameUnitPin = exports.setSubComponentSize = exports.setComponentSize = exports.setUnitSize = exports.setUnitMetadata = exports.setMetadata = exports.removeUnitPinData = exports.setUnitInputIgnored = exports.setUnitOutputIgnored = exports.setUnitPinIgnored = 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.exposeInput = exports.exposeInputSet = exports.exposePin = exports.setUnitId = exports.exposePinSet = exports.coverPin = exports.coverPinSet = exports.isPinMerged = exports.removePinFromMerge = exports._removePinFromMerge = exports.mergeMerges = exports.removeMerge = exports.addPinToMerge = exports.addMerge = exports.removeUnit = exports.removeUnitMerges = exports.removeUnitExposedPins = exports.removeUnitExposedTypePins = exports.mergeUnits = exports.removeUnits = exports.addUnit = exports.setName = void 0;
const deepGet_1 = require("../../deepGet");
const f_1 = require("../../system/core/object/ForEachKeyValue/f");
const f_2 = require("../../system/f/object/DeepMerge/f");
const f_3 = require("../../system/f/object/Merge/f");
const f_4 = require("../../system/f/object/Set/f");
const IOOf_1 = require("../../types/IOOf");
const array_1 = require("../../util/array");
const object_1 = require("../../util/object");
const component_1 = require("../util/component");
const spec_1 = require("../util/spec");
const component_2 = require("./component_");
const setName = ({ name }, spec) => {
    (0, object_1.deepSet)(spec, ['name'], name);
};
exports.setName = setName;
const addUnit = ({ unitId, unit }, spec) => {
    (0, object_1.deepSet)(spec, ['units', unitId], unit);
};
exports.addUnit = addUnit;
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_4.default)(spec, 'units', (0, f_3.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.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, object_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.deepDelete)(spec, ['merges', mergeId]);
                removedMergeIdSet.add(mergeId);
            }
        }
    });
    const reasignExposedPins = (type) => {
        var _a, _b, _c;
        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, object_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)((_a = mergeSpec === null || mergeSpec === void 0 ? void 0 : mergeSpec[otherMergeUnitId]) !== null && _a !== void 0 ? _a : {});
                        const otherMergeUnitPinId = (0, object_1.getObjSingleKey)((_c = (_b = mergeSpec === null || mergeSpec === void 0 ? void 0 : mergeSpec[otherMergeUnitId]) === null || _b === void 0 ? void 0 : _b[otherMergeUnitType]) !== null && _c !== void 0 ? _c : {});
                        (0, object_1.deepSet)(spec, [`${type}s`, exposedPinId, 'plug', subPinId], {
                            unitId: otherMergeUnitId,
                            pinId: otherMergeUnitPinId,
                        });
                    }
                    else {
                        //
                    }
                }
            }
        }
    };
    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, 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, type, subPinSpec.pinId], null)) {
                    (0, exports.coverPin)({ type, pinId, subPinId }, spec);
                    (0, exports.exposePin)({ type, pinId, subPinId, subPinSpec: { mergeId } }, spec);
                }
            });
        });
    });
    return (0, object_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`], ({ name, plug = {} }, exposedPinId) => {
            for (const subPinId in plug) {
                const subPin = plug[subPinId];
                const { unitId: _unitId, pinId: _pinId } = subPin;
                if (_unitId === unitId && _pinId === pinId && type === _type) {
                    (0, exports.unplugPin)({ pinId: exposedPinId, type: _type, subPinId }, spec);
                    (0, exports.plugPin)({
                        type: _type,
                        pinId: exposedPinId,
                        subPinId,
                        subPinSpec: { mergeId },
                    }, spec);
                }
            }
        });
    });
    return (0, object_1.deepSet)(spec, ['merges', mergeId, unitId, type, pinId], true);
};
exports.addPinToMerge = addPinToMerge;
const removeMerge = ({ mergeId }, spec) => {
    delete spec.merges[mergeId];
    spec.inputs = (0, object_1.mapObjVK)(spec.inputs, (input) => (Object.assign(Object.assign({}, input), { plug: (0, object_1.mapObjVK)(input.plug, (plug) => {
            return !plug.mergeId || mergeId !== plug.mergeId ? plug : {};
        }) })));
    spec.outputs = (0, object_1.mapObjVK)(spec.outputs, (output) => (Object.assign(Object.assign({}, output), { plug: (0, object_1.mapObjVK)(output.plug, (plug) => {
            return !plug.mergeId || mergeId !== plug.mergeId ? plug : {};
        }) })));
    if (spec.metadata && spec.metadata.position && spec.metadata.position.merge) {
        delete spec.metadata.position.merge[mergeId];
    }
};
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, object_1.deepSet)(spec, ['merges', a], (0, f_2.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 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) => {
    return (0, object_1.deepDelete)(spec, [`${type}s`, pinId]);
};
exports.coverPinSet = coverPinSet;
const coverPin = ({ pinId, type, subPinId, }, spec) => {
    (0, object_1.deepDelete)(spec, [`${type}s`, pinId, 'plug', subPinId]);
};
exports.coverPin = coverPin;
const exposePinSet = ({ pinId, type, pinSpec }, spec) => {
    return (0, object_1.deepSet)(spec, [`${type}s`, pinId], pinSpec);
};
exports.exposePinSet = exposePinSet;
const setUnitId = ({ unitId, newUnitId, name, }, 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, object_1.clone)(spec);
    const unitPlugs = (0, spec_1.findUnitPlugs)(specClone, unitId);
    const unitMerges = (0, spec_1.getUnitMergesSpec)(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);
            (0, component_2.removeSubComponentChild)({ subComponentId: parentId, childId: unitId }, spec.component);
            (0, component_2.insertSubComponentChild)({ parentId, childId: newUnitId, at }, spec.component);
        }
        else {
            const at = spec.component.children.indexOf(unitId);
            (0, component_2.removeChild)({ childId: unitId }, spec.component);
            (0, component_2.insertChild)({ 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, { type: _type, pinId: _pinId, subPinId: _subPinId }) => {
        const subPinSpec = (0, deepGet_1.default)(specClone, [
            `${_type !== null && _type !== void 0 ? _type : type}s`,
            _pinId,
            'plug',
            _subPinId,
        ]);
        if (subPinSpec.unitId === unitId) {
            subPinSpec.unitId = newUnitId;
        }
        (0, object_1.deepSet)(spec, [`${_type !== null && _type !== void 0 ? _type : type}s`, _pinId, 'plug', _subPinId], subPinSpec);
    });
};
exports.setUnitId = setUnitId;
const exposePin = ({ pinId, type, subPinId, subPinSpec, }, spec) => {
    return (0, object_1.deepSet)(spec, [`${type}s`, pinId, 'plug', subPinId], subPinSpec);
};
exports.exposePin = exposePin;
const exposeInputSet = ({ pinId, input }, spec) => (0, exports.exposePinSet)({ pinId, pinSpec: input, type: 'input' }, spec);
exports.exposeInputSet = exposeInputSet;
const exposeInput = ({ pinId, subPinId, input, }, spec) => {
    return (0, exports.exposePin)({ pinId, subPinId, subPinSpec: input, type: 'input' }, spec);
};
exports.exposeInput = exposeInput;
const plugPin = ({ type, pinId, subPinId, subPinSpec, }, spec) => {
    return (0, object_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, object_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, newId }, spec) => {
    const pinSpec = (0, deepGet_1.default)(spec, [`${type}s`, pinId]);
    (0, object_1.deepDelete)(spec, [`${type}s`, pinId]);
    (0, object_1.deepSet)(spec, [`${type}s`, newId], pinSpec);
};
exports.setPinSetId = setPinSetId;
const setPinSetFunctional = ({ type, pinId, functional }, spec) => {
    return (0, object_1.deepSet)(spec, [`${type}s`, pinId, 'functional'], functional);
};
exports.setPinSetFunctional = setPinSetFunctional;
const setPinSetDataType = ({ type, pinId, dataType }, spec) => {
    return (0, object_1.deepSet)(spec, [`${type}s`, pinId, 'type'], dataType);
};
exports.setPinSetDataType = setPinSetDataType;
const setPinSetRef = ({ type, pinId, ref }, spec) => {
    return (0, object_1.deepSet)(spec, [`${type}s`, pinId, 'ref'], ref);
};
exports.setPinSetRef = setPinSetRef;
const setUnitInput = ({ id, name, data }, spec) => {
    return (0, object_1.deepSet)(spec, ['units', id, 'input', name], data);
};
exports.setUnitInput = setUnitInput;
const setUnitOutput = ({ id, name, data }, spec) => {
    return (0, object_1.deepSet)(spec, ['units', id, 'output', name], data);
};
exports.setUnitOutput = setUnitOutput;
const setUnitPinData = ({ unitId, type, pinId, data, }, spec) => {
    return (0, object_1.deepSet)(spec, ['units', unitId, type, pinId, 'data'], data);
};
exports.setUnitPinData = setUnitPinData;
const setUnitPinConstant = ({ unitId, type, pinId, constant, }, spec) => {
    return (0, object_1.deepSet)(spec, ['units', unitId, type, pinId, 'constant'], constant);
};
exports.setUnitPinConstant = setUnitPinConstant;
const setUnitPinIgnored = ({ unitId, type, pinId, ignored, }, spec) => {
    var _a, _b, _c;
    if (ignored) {
        (0, spec_1.forEachPinOnMerges)((_a = spec.merges) !== null && _a !== void 0 ? _a : {}, (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)((_b = spec.inputs) !== null && _b !== void 0 ? _b : {}, ({ 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)((_c = spec.outputs) !== null && _c !== void 0 ? _c : {}, ({ plug }, _pinId) => {
                (0, f_1.default)(plug, (output, subPinId) => {
                    if (output.unitId === unitId && output.pinId === pinId) {
                        (0, exports.unplugOutput)({ pinId: _pinId, subPinId }, spec);
                    }
                });
            });
        }
    }
    (0, object_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, object_1.deepSet)(spec, path, value);
};
exports.setMetadata = setMetadata;
const setUnitMetadata = ({ id, path, value }, spec) => {
    (0, object_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;
    for (const unitId in units) {
        const unit = units[unitId];
        // TODO
    }
    (0, spec_1.forEachPinOnMerges)((0, object_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, object_1.deepSet)(spec, ['merges', mergeId, unitId, type, newPinId], true);
        }
    });
};
exports.renameUnitPin = renameUnitPin;
//# sourceMappingURL=spec_.js.map