import { Edge, GeomEdge, GeomGraph, Graph, Node, GeomNode } from '..';
import { Curve, PointLocation } from '../math/geometry';
import { HitTestBehavior } from '../math/geometry/RTree/HitTestBehavior';
import { CreateRectangleNodeOnData } from '../math/geometry/RTree/RectangleNode';
import { CrossRectangleNodesSameType } from '../math/geometry/RTree/RectangleNodeUtils';
import { GetConnectedComponents } from '../math/graphAlgorithms/ConnectedComponentCalculator';
import { mkGraphOnEdgesN } from '../structs/basicGraphOnEdges';
import { IntPair } from '../utils/IntPair';
import { PreGraph } from './PreGraph';
import { BundleRouter } from './spline/bundling/BundleRouter';
import { SdShortestPath } from './spline/bundling/SdShortestPath';
export class MultiEdgeRouter {
    constructor(multiEdgeGeoms, interactiveEdgeRouter, nodeBoundaryCurves, bundlingSettings, transparentShapeSetter) {
        this.multiEdgeGeometries = multiEdgeGeoms.map((l) => l.map((e) => e.edgeGeometry));
        this.interactiveEdgeRouter = interactiveEdgeRouter;
        this.bundlingSettings = bundlingSettings;
        this.transparentShapeSetter = transparentShapeSetter;
        this.nodeTree = CreateRectangleNodeOnData(nodeBoundaryCurves, (c) => c.boundingBox);
    }
    run() {
        for (const graph of this.GetGeometryGraphs()) {
            const br = new BundleRouter(Array.from(graph.edges()), new SdShortestPath(this.transparentShapeSetter, null, null), this.interactiveEdgeRouter.VisibilityGraph, this.bundlingSettings, this.interactiveEdgeRouter.LoosePadding, this.interactiveEdgeRouter.TightHierarchy, this.interactiveEdgeRouter.LooseHierarchy, null, null, null);
            br.run();
        }
    }
    *GetGeometryGraphs() {
        for (const preGraph of this.GetIndependantPreGraphs())
            yield this.CreateGeometryGraph(preGraph);
    }
    CreateGeometryGraph(preGraph) {
        const graph = new Graph();
        const gg = new GeomGraph(graph);
        const nodeDictionary = new Map();
        let i = 0;
        for (const curve of preGraph.nodeBoundaries) {
            const node = new Node(i.toString());
            i++;
            nodeDictionary.set(curve, node);
            graph.nodeCollection.addNode(node);
            new GeomNode(node).boundaryCurve = curve;
        }
        for (const eg of preGraph.edgeGeometries) {
            this.AddEdgeGeometryToGraph(eg, nodeDictionary, gg);
        }
        return gg;
    }
    AddEdgeGeometryToGraph(eg, nodeDictionary, gg) {
        const sourceNode = this.GetOrCreateNode(eg.sourcePort, nodeDictionary, gg);
        const targetNode = this.GetOrCreateNode(eg.targetPort, nodeDictionary, gg);
        const e = new Edge(sourceNode, targetNode);
        new GeomEdge(e).edgeGeometry = eg;
    }
    GetOrCreateNode(port, nodeDictionary, gg) {
        const curve = this.GetPortCurve(port);
        let node = nodeDictionary.get(curve);
        if (!node) {
            node = new Node(nodeDictionary.size.toString());
            const geomNode = new GeomNode(node);
            geomNode.boundaryCurve = curve;
            gg.graph.nodeCollection.addNode(node);
            nodeDictionary.set(curve, node);
        }
        return node;
    }
    GetPortCurve(port) {
        const curve = this.nodeTree.FirstHitNodeWithPredicate(port.Location, (point, c) => Curve.PointRelativeToCurveLocation(point, c) != PointLocation.Outside ? HitTestBehavior.Stop : HitTestBehavior.Continue).UserData;
        return curve;
    }
    //  creates a set of pregraphs suitable for bundle routing
    GetIndependantPreGraphs() {
        const preGraphs = this.CreateInitialPregraphs();
        do {
            const count = preGraphs.length;
            const t = { preGraphs: preGraphs };
            this.UniteConnectedPreGraphs(t);
            if (count <= preGraphs.length)
                break;
        } while (true);
        return preGraphs;
    }
    UniteConnectedPreGraphs(t) {
        const intersectionGraph = MultiEdgeRouter.GetIntersectionGraphOfPreGraphs(t.preGraphs);
        if (intersectionGraph == null)
            return;
        const connectedComponents = GetConnectedComponents(intersectionGraph);
        const newPreGraphList = new Array();
        for (const component of connectedComponents) {
            let preGraph = null;
            for (const i of component) {
                if (preGraph == null) {
                    preGraph = t.preGraphs[i];
                    newPreGraphList.push(preGraph);
                }
                else {
                    preGraph.AddGraph(t.preGraphs[i]);
                }
            }
        }
        t.preGraphs = newPreGraphList;
        for (const pg of t.preGraphs)
            this.AddIntersectingNodes(pg);
    }
    AddIntersectingNodes(pg) {
        const rect = pg.boundingBox;
        for (const curve of this.nodeTree.GetNodeItemsIntersectingRectangle(rect)) {
            pg.AddNodeBoundary(curve);
        }
    }
    static GetIntersectionGraphOfPreGraphs(preGraphs) {
        const intersectingPairs = MultiEdgeRouter.EnumeratePairsOfIntersectedPreGraphs(preGraphs);
        if (intersectingPairs.length) {
            return mkGraphOnEdgesN(intersectingPairs, preGraphs.length);
        }
        return null;
    }
    static EnumeratePairsOfIntersectedPreGraphs(preGraphs) {
        const arr = Array.from(Array(preGraphs.length).keys());
        const rn = CreateRectangleNodeOnData(arr, (i) => preGraphs[i].boundingBox);
        const list = new Array();
        CrossRectangleNodesSameType(rn, rn, (i, j) => list.push(new IntPair(i, j)));
        return list;
    }
    CreateInitialPregraphs() {
        return this.multiEdgeGeometries.map((a) => this.CreatePregraphFromSetOfEdgeGeometries(a));
    }
    CreatePregraphFromSetOfEdgeGeometries(egs) {
        const nodeBoundaries = new Set();
        const eg = egs[0];
        const c = this.GetPortCurve(eg.sourcePort);
        const rect = c.boundingBox;
        nodeBoundaries.add(c);
        nodeBoundaries.add(eg.targetPort.Curve);
        rect.addRec(eg.targetPort.Curve.boundingBox);
        const overlapped = this.nodeTree.GetNodeItemsIntersectingRectangle(rect);
        for (const nodeBoundary of overlapped)
            nodeBoundaries.add(nodeBoundary);
        return PreGraph.constructorStatic(egs, nodeBoundaries);
    }
}
//# sourceMappingURL=MultiEdgeRouter.js.map