"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.moveSubgraph = exports.movePlug = exports.moveMerge = exports.moveLinkPinInto = exports.moveUnit = void 0;
const SELF_1 = require("../../constant/SELF");
const deepGet_1 = require("../../deepGet");
const spec_1 = require("../../spec/util/spec");
const f_1 = require("../../system/core/object/ForEachKeyValue/f");
const f_2 = require("../../system/core/object/KeyCount/f");
const IOOf_1 = require("../../types/IOOf");
const object_1 = require("../../util/object");
const isRefMerge_1 = require("./isRefMerge");
function moveUnit(source, target, graphId, unitId, collapseMap, connectOpt, ignoredUnit, unitIgnoredPin, ignoredMerge, pinSpecs, reverse) {
    var _a;
    const { nextIdMap, nextPinIdMap, nextSubComponentParentMap, nextSubComponentChildrenMap, } = collapseMap;
    const unit = source.getUnit(unitId);
    const nextUnitId = ((_a = nextIdMap.unit) === null || _a === void 0 ? void 0 : _a[unitId]) || unitId;
    const nextSubComponentParentId = nextSubComponentParentMap[unitId] || null;
    const nextSubComponentChildren = nextSubComponentChildrenMap[unitId] || [];
    const nextUnitPinMap = nextPinIdMap[unitId] || {};
    const ignoredPin = unitIgnoredPin[unitId] || {
        input: new Set(),
        output: new Set(),
    };
    const spec = source.getSpec();
    const { units, component = {} } = spec;
    const { subComponents = {} } = component;
    const unitSpec = units[unitId];
    const subComponent = subComponents[unitId];
    source.removeUnit(unitId, false, false, false);
    target.addUnit(nextUnitId, unit, undefined, false);
    if (nextSubComponentParentId) {
        if (target.hasUnit(nextSubComponentParentId)) {
            const to = nextSubComponentChildrenMap[nextSubComponentParentId].indexOf(nextUnitId);
            target.moveRoot(nextSubComponentParentId, nextUnitId, to, 'default');
        }
    }
    if (nextSubComponentChildren) {
        for (let i = 0; i < nextSubComponentChildren.length; i++) {
            const nextSubComponentChildId = nextSubComponentChildren[i];
            if (target.hasUnit(nextSubComponentChildId)) {
                target.moveRoot(nextUnitId, nextSubComponentChildId, i, 'default');
            }
        }
    }
    if (subComponent) {
        const { metadata: { component } = {} } = unitSpec;
        if (component &&
            typeof component.width === 'number' &&
            typeof component.height === 'number') {
            target.setUnitSize(nextUnitId, component.width, component.height);
        }
    }
    const moveUnitPin = (type, pinId) => {
        var _a, _b;
        const { pinId: nextPinId, subPinId: nextSubPinId, plug, mergeId, merge, } = (0, object_1.deepGetOrDefault)(nextUnitPinMap, [type, pinId], {});
        if (!ignoredPin[type].has(pinId) && !unit.isPinIgnored(type, pinId)) {
            const shouldSwapMergePin = mergeId && (!ignoredMerge.has(mergeId) || reverse);
            if (reverse) {
                //
            }
            else {
                if (target.hasPinNamed(type, nextPinId)) {
                    //
                }
                else {
                    (0, object_1.forEachObjVK)(pinSpecs[type] || {}, ({ plug = {} }, id) => {
                        for (const subPinId in plug) {
                            const subPinSpec = plug[subPinId];
                            if (subPinSpec.unitId === unitId && subPinSpec.pinId === pinId) {
                                source.unplugPin(type, id, subPinId, false, false);
                                break;
                            }
                        }
                    });
                }
            }
            if (nextPinId && nextSubPinId) {
                if (reverse) {
                    if (mergeId) {
                        if (target.hasMergePin(mergeId, graphId, type, nextPinId)) {
                            target.removePinOrMerge(mergeId, graphId, type, nextPinId, false, false);
                        }
                    }
                    const { pinId: _pinId, subPinId: _subPinId } = ((_b = (_a = connectOpt.plugs) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b[pinId]) || {};
                    if (_pinId && _subPinId && _pinId === nextPinId) {
                        if (target.hasPinNamed(type, _pinId)) {
                            if (target.hasPlug(type, _pinId, _subPinId)) {
                                target.unplugPin(type, _pinId, _subPinId, false, false);
                            }
                            target.plugPin(type, _pinId, _subPinId, {
                                unitId: nextUnitId,
                                pinId,
                            }, false, false);
                        }
                        else {
                            target.exposePinSet(type, _pinId, {
                                plug: {
                                    [_subPinId]: {
                                        unitId: nextUnitId,
                                        pinId,
                                    },
                                },
                            }, undefined, false, false);
                        }
                    }
                }
                else {
                    if (target.hasPinNamed(type, nextPinId)) {
                        target.exposePin(type, nextPinId, nextSubPinId, {
                            unitId: nextUnitId,
                            pinId,
                        }, false, false);
                    }
                    else {
                        const ref = unit.isPinRef(type, pinId);
                        const data = unit.getPinData(type, pinId);
                        target.exposePinSet(type, nextPinId, {
                            plug: {
                                '0': {
                                    unitId: nextUnitId,
                                    pinId,
                                },
                            },
                            ref,
                        }, data, false, false);
                        (0, f_1.default)(pinSpecs[type] || {}, ({ plug }, id) => {
                            for (const subPinId in plug) {
                                const subPinSpec = plug[subPinId];
                                if (subPinSpec.unitId === unitId &&
                                    subPinSpec.pinId === pinId) {
                                    source.plugPin(type, id, subPinId, {
                                        unitId: graphId,
                                        pinId: nextPinId,
                                    }, false, false);
                                    break;
                                }
                            }
                        });
                    }
                }
                const constant = unit.isPinConstant(type, pinId);
                if (constant) {
                    if (reverse) {
                        //
                    }
                    else {
                        target.setUnitPinConstant(unitId, type, pinId, false, false);
                        target.setPinConstant(type, nextPinId, true);
                    }
                }
                if (shouldSwapMergePin) {
                    if (reverse) {
                        if (target.hasMergePin(mergeId, graphId, type, pinId)) {
                            target.removePinOrMerge(mergeId, graphId, type, pinId, false, false);
                        }
                        if (!target.hasMerge(mergeId)) {
                            target.addMerge(merge !== null && merge !== void 0 ? merge : {}, mergeId, false, false);
                        }
                        if (!target.hasMergePin(mergeId, nextUnitId, type, pinId)) {
                            target.addPinToMerge(mergeId, nextUnitId, type, pinId, false, false);
                        }
                    }
                    else {
                        if (!source.hasMerge(mergeId)) {
                            source.addMerge({}, mergeId, false, false);
                            (0, spec_1.forEachPinOnMerge)(merge, (unitId, type, pinId) => {
                                if (source.hasUnit(unitId)) {
                                    source.addPinToMerge(mergeId, unitId, type, pinId, false, false);
                                }
                            });
                        }
                        if (!source.hasMergePin(mergeId, graphId, type, nextPinId)) {
                            source.addPinToMerge(mergeId, graphId, type, nextPinId, false, false);
                        }
                    }
                }
            }
        }
        if (plug) {
            const { type, pinId, subPinId, kind = type } = plug;
            if (reverse) {
                //
            }
            else {
                if (target.hasPlug(type, pinId, subPinId)) {
                    target.plugPin(type, pinId, subPinId, {
                        unitId: nextUnitId,
                        pinId,
                        kind,
                    });
                }
                else {
                    //
                }
            }
        }
    };
    const inputs = unit.getInputNames();
    for (const input_id of inputs) {
        moveUnitPin('input', input_id);
    }
    const outputs = unit.getOutputNames();
    for (const output_id of outputs) {
        moveUnitPin('output', output_id);
    }
    moveUnitPin('output', SELF_1.SELF);
}
exports.moveUnit = moveUnit;
function moveLinkPinInto(source, target, graphId, unitId, type, pinId, data, collapseMap, oppositeMergeId, oppositePinId, plugPinSpec, ignoredUnit = new Set(), reverse) {
    var _a, _b;
    if (ignoredUnit.has(unitId) && graphId !== unitId) {
        return;
    }
    const { nextPinIdMap } = collapseMap;
    const { mergeId, merge } = (0, object_1.deepGetOrDefault)(nextPinIdMap, [type, pinId], {
        pinId: undefined,
        subPinId: undefined,
    });
    if (graphId === unitId) {
        if (reverse) {
            //
        }
        else {
            const constant = target.isPinConstant(type, pinId);
            const pinSpec = (0, object_1.clone)(target.getExposedPinSpec(type, pinId));
            if (mergeId && merge) {
                const mergeUnit = merge[unitId];
                const mergePinCount = (0, spec_1.getMergePinCount)(merge);
                const unitMergePinCount = (0, spec_1.getMergeUnitPinCount)(mergeUnit);
                if (mergePinCount - unitMergePinCount > 0) {
                    //
                }
                else {
                    target.coverPinSet(type, pinId, false);
                }
            }
            else {
                target.coverPinSet(type, pinId, false);
            }
            if (constant) {
                const { plug = {} } = pinSpec;
                for (const subPinId in plug) {
                    const subPinSpec = plug[subPinId];
                    if (subPinSpec.unitId && subPinSpec.pinId) {
                        target.setUnitPinConstant(subPinSpec.unitId, type, subPinSpec.pinId, true);
                    }
                    else if (subPinSpec.mergeId) {
                        const mergeSpec = target.getMergeSpec(subPinSpec.mergeId);
                        (0, spec_1.forEachPinOnMerge)(mergeSpec, (unitId, type, pinId) => {
                            target.setUnitPinConstant(unitId, type, pinId, true);
                        });
                    }
                }
            }
        }
    }
    else {
        if (oppositeMergeId && oppositePinId) {
            const oppositeType = (0, spec_1.opposite)(type);
            if (reverse) {
                if (source.hasPinNamed(oppositeType, oppositePinId)) {
                    source.coverPinSet(oppositeType, oppositePinId, false);
                }
                else {
                    //
                }
            }
            else {
                if (target.hasPinNamed(oppositeType, oppositePinId)) {
                    //
                }
                else {
                    const unit = source.getUnit(unitId);
                    data = data !== null && data !== void 0 ? data : source.getUnitPinData(unitId, type, pinId);
                    const ref = unit.isPinRef(type, pinId);
                    const pinSpec = { plug: { '0': {} }, ref };
                    target.exposePinSet(oppositeType, oppositePinId, pinSpec, data, false, false);
                }
                if (source.hasMerge(oppositeMergeId)) {
                    const merge = source.getMergeSpec(oppositeMergeId);
                    if (!((_b = (_a = merge === null || merge === void 0 ? void 0 : merge[graphId]) === null || _a === void 0 ? void 0 : _a[oppositeType]) === null || _b === void 0 ? void 0 : _b[oppositePinId])) {
                        source.addPinToMerge(oppositeMergeId, graphId, oppositeType, oppositePinId, false, false);
                    }
                    source.addPinToMerge(oppositeMergeId, unitId, type, pinId, false, false);
                }
                else {
                    const merge = {
                        [unitId]: {
                            [type]: {
                                [pinId]: true,
                            },
                        },
                        [graphId]: {
                            [oppositeType]: {
                                [oppositePinId]: true,
                            },
                        },
                    };
                    source.addMerge(merge, oppositeMergeId, false, false);
                }
            }
        }
        else {
            //
        }
        if (plugPinSpec) {
            const nextUnitId = unitId;
            const newPinSpec = graphId !== unitId
                ? { plug: { '0': { unitId: nextUnitId, pinId } } }
                : { plug: { '0': {} } };
            target.exposePinSet(type, plugPinSpec.pinId, newPinSpec, undefined, false, false);
            source.plugPin(type, plugPinSpec.pinId, plugPinSpec.subPinId, {
                unitId: graphId,
                pinId: plugPinSpec.pinId,
            });
        }
    }
}
exports.moveLinkPinInto = moveLinkPinInto;
function moveMerge(source, target, graphId, mergeId, mergeSpec, mergeIsRef, collapseMap, connectOpt, ignoredUnit = new Set(), pinSpecs, reverse) {
    var _a;
    const { nextIdMap, nextMergePinId } = collapseMap;
    const nextMergeId = (_a = nextIdMap.merge[mergeId]) !== null && _a !== void 0 ? _a : mergeId;
    const { input: nextInput = null, output: nextOutput = null } = nextMergePinId[mergeId] || {};
    let pinIntoCount = 0;
    const sourceMergeSpec = source.getMergeSpec(mergeId);
    const nextMerge = {};
    const { merges: graphMerges } = connectOpt;
    const mergePinCount = (0, spec_1.getMergePinCount)(mergeSpec);
    const mergeInputCount = (0, spec_1.getMergeTypePinCount)(mergeSpec, 'input');
    const mergeOutputCount = (0, spec_1.getMergeTypePinCount)(mergeSpec, 'output');
    const data = source.getMergeData(mergeId);
    if (source.hasMerge(mergeId)) {
        source.removeMerge(mergeId, false, false);
    }
    const moveMergePin = (unitId, type, pinId) => {
        var _a, _b;
        const nextUnitId = ((_a = nextIdMap.unit) === null || _a === void 0 ? void 0 : _a[unitId]) || unitId;
        if (unitId === graphId && !ignoredUnit.has(unitId)) {
            const pinSpec = target.getExposedPinSpec(type, pinId);
            const { plug } = pinSpec;
            for (const subPinId in plug) {
                const subPin = plug[subPinId];
                if (subPin.unitId && subPin.pinId) {
                    (0, object_1.deepSet)(nextMerge, [subPin.unitId, type, subPin.pinId], true);
                }
                else if (subPin.mergeId) {
                    const mergeSpec = target.getMergeSpec(subPin.mergeId);
                    (0, spec_1.forEachPinOnMerge)(mergeSpec, (unitId, type, pinId) => {
                        (0, object_1.deepSet)(nextMerge, [nextUnitId, type, pinId], true);
                    });
                }
            }
            pinIntoCount++;
        }
        else if (ignoredUnit.has(unitId)) {
            if (target.hasUnit(nextUnitId)) {
                (0, object_1.deepSet)(nextMerge, [nextUnitId, type, pinId], true);
                pinIntoCount++;
            }
        }
        else {
            //
        }
        const isInput = type === 'input';
        const pickInput = !isInput && !ignoredUnit.has(unitId);
        const { mergeId: nextMergeId, pinId: nextPinId, subPinSpec: nextSubPinSpec, } = (_b = (pickInput ? nextInput : nextOutput)) !== null && _b !== void 0 ? _b : {};
        moveLinkPinInto(source, target, graphId, unitId, type, pinId, data, collapseMap, nextMergeId, nextPinId, null, ignoredUnit, reverse);
    };
    (0, spec_1.forEachPinOnMerge)(mergeSpec, moveMergePin);
    if (reverse) {
        if (mergePinCount === 0 || pinIntoCount > 1) {
            // AD HOC
            const propagate = mergeIsRef;
            target.addMerge(nextMerge, nextMergeId, false, propagate);
        }
    }
    else {
        if ((pinIntoCount === 0 &&
            (mergePinCount === 0 ||
                (mergeInputCount > 0 && mergeOutputCount > 0))) ||
            pinIntoCount > 1) {
            // AD HOC
            const propagate = mergeIsRef;
            target.addMerge(nextMerge, nextMergeId, false, propagate);
            if ((0, f_2.keyCount)(mergeSpec !== null && mergeSpec !== void 0 ? mergeSpec : {}) === 1 &&
                (0, object_1.getObjSingleKey)(mergeSpec) === graphId) {
                (0, spec_1.forEachPinOnMerge)(mergeSpec, (_graphId, type, pinId) => {
                    if (target.hasPinNamed(type, pinId)) {
                        target.coverPinSet(type, pinId);
                    }
                });
            }
        }
    }
    const processMergePin = (type, nextPin) => {
        var _a, _b, _c;
        const { mergeId: _mergeId, pinId, subPinSpec, oppositeMerge } = nextPin;
        if (pinId && subPinSpec) {
            if (reverse) {
                if (target.hasMergePin(_mergeId, graphId, type, pinId)) {
                    target.removeMerge(_mergeId, false, false);
                }
                for (const graphMergeId in graphMerges) {
                    const merge = graphMerges[graphMergeId];
                    const graphMerge = merge[graphId];
                    if ((_a = graphMerge === null || graphMerge === void 0 ? void 0 : graphMerge.output) === null || _a === void 0 ? void 0 : _a[SELF_1.SELF]) {
                        return;
                    }
                    for (const graphPinType in graphMerge) {
                        const graphMergeTypePins = graphMerge[graphPinType];
                        for (const graphPinId in graphMergeTypePins) {
                            if (graphPinId !== pinId) {
                                continue;
                            }
                            const pinSpec = pinSpecs[graphPinType][graphPinId];
                            const { plug } = pinSpec;
                            for (const subPinId in plug) {
                                const subPinSpec = plug[subPinId];
                                if (subPinSpec.mergeId) {
                                    const newMergeId = ((_b = nextIdMap.merge) === null || _b === void 0 ? void 0 : _b[subPinSpec.mergeId]) || subPinSpec.mergeId;
                                    const mergeClone = (0, object_1.clone)(merge);
                                    delete mergeClone[graphId];
                                    const otherUnitId = (0, object_1.getObjSingleKey)(mergeClone);
                                    const otherUnitPinType = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId]);
                                    const otherUnitPinId = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId][otherUnitPinType]);
                                    if (target.hasMerge(newMergeId)) {
                                        (0, spec_1.forEachPinOnMerge)(mergeClone, (unitId, unitPinType, pinId) => {
                                            if (unitPinType !== type &&
                                                !target.hasMergePin(newMergeId, unitId, unitPinType, pinId)) {
                                                target.addPinToMerge(newMergeId, unitId, unitPinType, pinId, false);
                                            }
                                        });
                                    }
                                    else {
                                        const newMergeSpec = {
                                            [otherUnitId]: {
                                                [otherUnitPinType]: { [otherUnitPinId]: true },
                                            },
                                        };
                                        if (!target.hasMerge(newMergeId)) {
                                            target.addMerge(newMergeSpec, newMergeId, false, false);
                                        }
                                    }
                                }
                                else if (subPinSpec.unitId && subPinSpec.pinId) {
                                    const newUnitId = ((_c = nextIdMap.unit) === null || _c === void 0 ? void 0 : _c[subPinSpec.unitId]) || subPinSpec.unitId;
                                    if (target.hasMerge(graphMergeId)) {
                                        target.addPinToMerge(graphMergeId, newUnitId, graphPinType, subPinSpec.pinId, false);
                                    }
                                    else {
                                        if (oppositeMerge) {
                                            const mergeClone = (0, object_1.clone)(oppositeMerge);
                                            delete mergeClone[subPinSpec.unitId];
                                            const otherUnitId = (0, object_1.getObjSingleKey)(mergeClone);
                                            if (!otherUnitId) {
                                                continue;
                                            }
                                            const otherUnitPinType = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId]);
                                            const otherUnitPinId = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId][otherUnitPinType]);
                                            target.addMerge({
                                                [newUnitId]: {
                                                    [graphPinType]: { [subPinSpec.pinId]: true },
                                                },
                                                [otherUnitId]: {
                                                    [otherUnitPinType]: { [otherUnitPinId]: true },
                                                },
                                            }, mergeId, false, false, undefined);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else {
                if (target.hasPinNamed(type, pinId)) {
                    target.exposePin(type, pinId, '0', subPinSpec, false, false);
                }
                else {
                    target.exposePinSet(type, pinId, {
                        plug: {
                            '0': subPinSpec,
                        },
                    }, undefined, false, false);
                }
                if (!source.hasMerge(_mergeId)) {
                    if (oppositeMerge) {
                        const mergeClone = (0, object_1.clone)(oppositeMerge);
                        delete mergeClone[subPinSpec.unitId];
                        (0, spec_1.forEachPinOnMerge)(oppositeMerge, (unitId) => {
                            if (ignoredUnit.has(unitId)) {
                                delete mergeClone[unitId];
                            }
                        });
                        const otherUnitId = (0, object_1.getObjSingleKey)(mergeClone);
                        if (otherUnitId) {
                            const otherUnitPinType = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId]);
                            const otherUnitPinId = (0, object_1.getObjSingleKey)(mergeClone[otherUnitId][otherUnitPinType]);
                            source.addMerge({
                                [graphId]: {
                                    [type]: { [pinId]: true },
                                },
                                [otherUnitId]: {
                                    [otherUnitPinType]: { [otherUnitPinId]: true },
                                },
                            }, _mergeId, false, false, undefined);
                        }
                    }
                }
            }
        }
    };
    nextInput && processMergePin('input', nextInput);
    nextOutput && processMergePin('output', nextOutput);
    (0, IOOf_1.forIO)(pinSpecs, (type, pinsSpec) => {
        (0, object_1.forEachObjKV)(pinsSpec, (pinId, pinSpec) => {
            var _a, _b;
            if ((0, object_1.deepGetOrDefault)(collapseMap, ['nextPlugSpec', type, pinId], undefined)) {
                return;
            }
            const { plug } = pinSpec;
            for (const subPinId in plug) {
                const subPinSpec = plug[subPinId];
                if (subPinSpec.mergeId === mergeId) {
                    const { mergeId: oppositeMergeId, oppositeMerge } = (_a = (type === 'input' ? nextInput : nextOutput)) !== null && _a !== void 0 ? _a : {};
                    if (reverse) {
                        if ((0, spec_1.isEmptyMerge)(sourceMergeSpec)) {
                            const targetPinSpecs = target.getExposedPinSpecs();
                            (0, object_1.forEachObjKV)((_b = targetPinSpecs[type]) !== null && _b !== void 0 ? _b : {}, (pinId, targetPinSpec) => {
                                const { plug = {} } = targetPinSpec;
                                for (const subPinId in plug) {
                                    const subPinSpec = plug[subPinId];
                                    if (subPinSpec.unitId === graphId &&
                                        subPinSpec.pinId === pinId) {
                                        target.unplugPin(type, pinId, subPinId, false, false);
                                        target.plugPin(type, pinId, subPinId, {
                                            mergeId: oppositeMergeId,
                                        }, false, false);
                                    }
                                }
                            });
                            if (!oppositeMergeId || !oppositeMerge) {
                                continue;
                            }
                            if (target.hasMerge(nextMergeId)) {
                                (0, spec_1.forEachPinOnMerge)(oppositeMerge, (unitId, type, pinId) => {
                                    if (unitId !== graphId) {
                                        if (!target.hasMergePin(nextMergeId, unitId, type, pinId)) {
                                            target.addPinToMerge(nextMergeId, unitId, type, pinId, false, false);
                                        }
                                    }
                                });
                            }
                            else {
                                target.addMerge(oppositeMerge, nextMergeId, false, false);
                            }
                        }
                    }
                    else {
                        if (source.hasPlug(type, pinId, subPinId)) {
                            const subPinSpec = oppositeMergeId
                                ? { mergeId: oppositeMergeId }
                                : { unitId: graphId, pinId };
                            const hasMerge = target.hasMerge(nextMergeId);
                            const data = source.getPinData(type, pinId);
                            if (!target.hasPinNamed(type, pinId)) {
                                target.exposePinSet(type, pinId, {
                                    plug: {
                                        [subPinId]: hasMerge ? { mergeId: nextMergeId } : {},
                                    },
                                }, data, false, false);
                            }
                            else {
                                target.plugPin(type, pinId, subPinId, hasMerge ? { mergeId: nextMergeId } : {}, false, false);
                            }
                            if (!source.hasPinNamed(type, pinId)) {
                                source.exposePinSet(type, pinId, {
                                    plug: {
                                        [subPinId]: subPinSpec,
                                    },
                                }, undefined, false, false);
                            }
                            else {
                                if (source.hasPlug(type, pinId, subPinId)) {
                                    source.unplugPin(type, pinId, subPinId, false, false);
                                }
                                source.plugPin(type, pinId, subPinId, subPinSpec, false, false);
                            }
                        }
                    }
                }
            }
        });
    });
}
exports.moveMerge = moveMerge;
function movePlug(source, target, graphId, type, pinId, pinSpec, subPinId, subPinSpec, nextPlugSpec, nextPinIdMap, nextMergePinId, nextIdMap) {
    var _a;
    const currentPinSpec = source.getExposedPinSpec(type, pinId);
    let data;
    if (currentPinSpec) {
        const plugCount = (0, f_2.keyCount)((_a = currentPinSpec.plug) !== null && _a !== void 0 ? _a : {});
        data = source.getPinData(type, pinId);
        if (plugCount === 1) {
            source.coverPinSet(type, pinId, false);
        }
        else {
            source.coverPin(type, pinId, subPinId, false);
        }
    }
    const nextType = (0, object_1.deepGetOrDefault)(nextIdMap, ['plug', type, pinId, subPinId, 'type'], type);
    const nextSubPinId = (0, object_1.deepGetOrDefault)(nextIdMap, ['plug', type, pinId, subPinId, 'subPinId'], subPinId);
    const nextSubPinSpec = (0, object_1.deepGetOrDefault)(nextPlugSpec, [type, pinId, subPinId], undefined);
    if (!subPinSpec) {
        source.exposePinSet(type, pinId, { plug: { [subPinId]: {} } }, undefined, false, false);
        return;
    }
    if (!nextSubPinSpec) {
        return;
    }
    const { pinId: nextPinId = subPinSpec.pinId } = nextSubPinSpec;
    let nextSubPinSpec_ = nextSubPinSpec;
    if (nextSubPinSpec.unitId &&
        nextSubPinSpec.pinId &&
        target.hasUnit(nextSubPinSpec.unitId)) {
        //
    }
    else if (nextSubPinSpec.mergeId &&
        target.hasMerge(nextSubPinSpec.mergeId)) {
        //
    }
    else {
        nextSubPinSpec_ = {};
    }
    if (target.hasPinNamed(nextType, pinId)) {
        if (target.hasPlug(nextType, pinId, nextSubPinId)) {
            target.plugPin(nextType, pinId, nextSubPinId, nextSubPinSpec_, false, false);
        }
        else {
            target.exposePin(nextType, pinId, nextSubPinId, nextSubPinSpec_, false, false);
        }
    }
    else {
        target.exposePinSet(nextType, pinId, {
            plug: {
                [nextSubPinId]: nextSubPinSpec_,
            },
            ref: !!pinSpec.ref,
        }, data, false, false);
    }
    if (!subPinSpec) {
        return;
    }
    if (subPinSpec.unitId && subPinSpec.pinId) {
        let nextMergeId = (0, object_1.deepGetOrDefault)(nextIdMap, ['link', subPinSpec.unitId, subPinSpec.type, pinId, 'mergeId'], null);
        if (nextMergeId) {
            source.addPinToMerge(nextMergeId, graphId, nextType, nextPinId, false, false);
        }
        else {
            nextMergeId = (0, object_1.deepGetOrDefault)(nextIdMap, ['plug', type, pinId, subPinId, 'mergeId'], null);
            if (nextMergeId) {
                if (source.hasMerge(nextMergeId)) {
                    source.addPinToMerge(nextMergeId, graphId, nextType, nextPinId, false, false);
                }
                else {
                    source.addMerge({
                        [graphId]: {
                            [nextType]: {
                                [nextPinId]: true,
                            },
                        },
                        [subPinSpec.unitId]: {
                            [type]: {
                                [subPinSpec.pinId]: true,
                            },
                        },
                    }, nextMergeId, false, false, undefined);
                }
            }
        }
    }
    else if (subPinSpec.mergeId) {
        const nextMergeId = (0, object_1.deepGetOrDefault)(nextMergePinId, ['merge', subPinSpec.mergeId, type], (0, object_1.deepGetOrDefault)(nextIdMap, ['plug', type, pinId, subPinId, 'mergeId'], null));
        if (nextMergeId) {
            source.addPinToMerge(nextMergeId, graphId, nextType, nextPinId !== null && nextPinId !== void 0 ? nextPinId : pinId, false, false);
        }
        else {
            //
        }
    }
}
exports.movePlug = movePlug;
function moveSubgraph(source, target, graphId, collapseMap, connectOpt, reverse = true) {
    const { nodeIds, nextIdMap, nextPinIdMap, nextMergePinId, nextPlugSpec, nextSubComponentParentMap, nextSubComponentChildrenMap, } = collapseMap;
    const { merge = [], link = [], unit = [], plug = [] } = nodeIds;
    const sourceMergeSpecs = (0, object_1.clone)(source.getMergesSpec());
    const sourcePinSpecs = (0, object_1.clone)(source.getExposedPinSpecs());
    const sourceMergeRefMap = (0, object_1.mapObjKV)(sourceMergeSpecs, (mergeId, mergeSpec) => {
        return (0, isRefMerge_1.isRefMerge)(source, mergeSpec);
    });
    const ignoredUnitPin = {};
    const ignoredUnit = new Set(unit);
    const ignoredMerge = new Set(merge);
    const setUnitPinIgnored = (unitId, type, pinId) => {
        // console.log('setUnitPinIgnored', unitId, type, pinId)
        ignoredUnitPin[unitId] = ignoredUnitPin[unitId] || {
            input: new Set(),
            output: new Set(),
        };
        ignoredUnitPin[unitId][type].add(pinId);
    };
    const findUnitPinPlug = (unitId_, type_, pinId_) => {
        let plugSpec;
        (0, IOOf_1.forIOObjKV)(sourcePinSpecs, (type, pinId, pinSpec) => {
            const { plug } = pinSpec;
            for (const subPinId in plug) {
                const subPinSpec = plug[subPinId];
                if (subPinSpec.unitId === unitId_ && subPinSpec.pinId === pinId_) {
                    plugSpec = {
                        type,
                        pinId,
                        subPinId,
                    };
                }
            }
        });
        return plugSpec;
    };
    for (const { unitId, type, pinId } of link) {
        const pinPlug = findUnitPinPlug(unitId, type, pinId);
        if (pinPlug &&
            !plug.find((plugObj) => {
                return (plugObj.type === pinPlug.type &&
                    plugObj.pinId === pinPlug.pinId &&
                    plugObj.subPinId === pinPlug.subPinId);
            })) {
            continue;
        }
        setUnitPinIgnored(unitId, type, pinId);
    }
    const nextMergeSpecs = {};
    for (const mergeId of merge) {
        if (source.hasMerge(mergeId)) {
            const mergeSpec = source.getMergeSpec(mergeId);
            nextMergeSpecs[mergeId] = mergeSpec;
            (0, spec_1.forEachPinOnMerge)(mergeSpec, (unitId, type, pinId) => {
                setUnitPinIgnored(unitId, type, pinId);
            });
        }
    }
    for (const { unitId, type, pinId } of link) {
        const { mergeId, oppositePinId } = (0, object_1.deepGetOrDefault)(nextIdMap, ['link', unitId, type, pinId], { mergeId: null, oppositePinId: null });
        const plugPinSpec = null;
        moveLinkPinInto(source, target, graphId, unitId, type, pinId, undefined, collapseMap, mergeId, oppositePinId, plugPinSpec, ignoredUnit, reverse);
    }
    for (const unitId of unit) {
        moveUnit(source, target, graphId, unitId, collapseMap, connectOpt, ignoredUnit, ignoredUnitPin, ignoredMerge, sourcePinSpecs, reverse);
    }
    for (const mergeId of merge) {
        const mergeSpec = sourceMergeSpecs[mergeId];
        const mergeIsRef = sourceMergeRefMap[mergeId];
        moveMerge(source, target, graphId, mergeId, mergeSpec, mergeIsRef, collapseMap, connectOpt, ignoredUnit, sourcePinSpecs, reverse);
    }
    for (const { type, pinId, subPinId } of plug) {
        const pinSpec = (0, deepGet_1.default)(sourcePinSpecs, [type, pinId]);
        const subPinSpec = (0, object_1.deepGetOrDefault)(sourcePinSpecs, [type, pinId, 'plug', subPinId], undefined);
        movePlug(source, target, graphId, type, pinId, pinSpec, subPinId, subPinSpec, nextPlugSpec, nextPinIdMap, nextMergePinId, nextIdMap);
    }
}
exports.moveSubgraph = moveSubgraph;
//# sourceMappingURL=moveSubgraph.js.map