/// Avoid a situation where two paths cross each other more than once. Remove self loops.
///
// import {Assert} from '../../../utils/assert'
import { PointMap } from '../../../utils/PointMap';
import { LinkedPoint } from './LinkedPoint';
export class PathMerger {
    constructor(paths) {
        this.verticesToPathOffsets = new PointMap();
        this.Paths = paths;
    }
    ///  Avoid a situation where two paths cross each other more than once. Remove self loops.
    MergePaths() {
        this.InitVerticesToPathOffsetsAndRemoveSelfCycles();
        for (const path of this.Paths) {
            this.ProcessPath(path);
        }
    }
    ProcessPath(path) {
        const departedPaths = new Map();
        let prevLocationPathOffsets = null;
        for (let linkedPoint = path.PathPoints; linkedPoint != null; linkedPoint = linkedPoint.Next) {
            const pathOffsets = this.verticesToPathOffsets.get(linkedPoint.Point);
            if (prevLocationPathOffsets != null) {
                // handle returning paths
                if (departedPaths.size > 0) {
                    for (const [path0, v] of pathOffsets) {
                        const departerLinkedPoint = departedPaths.get(path0);
                        if (departerLinkedPoint) {
                            // returned!
                            this.CollapseLoopingPath(path0, departerLinkedPoint, v, path, linkedPoint);
                            departedPaths.delete(path0);
                        }
                    }
                }
                // find departed paths
                for (const [k, v] of prevLocationPathOffsets) {
                    if (!pathOffsets.has(k))
                        departedPaths.set(k, v);
                }
            }
            prevLocationPathOffsets = pathOffsets;
        }
    }
    //         bool Correct() {
    //             foreach (var kv of verticesToPathOffsets) {
    //                 Point p = kv.Key;
    //                 Map<Path, LinkedPoint> pathOffs = kv.Value;
    //                 foreach (var pathOff of pathOffs) {
    //                     var path = pathOff.Key;
    //                     var linkedPoint = pathOff.Value;
    //                     if (linkedPoint.Point != p)
    //                         return false;
    //                     if (FindLinkedPointInPath(path, p) == null) {
    //                         return false;
    //                     }
    //                 }
    //             }
    //             return true;
    //         }
    CollapseLoopingPath(loopingPath, departureFromLooping, arrivalToLooping, stemPath, arrivalToStem) {
        const departurePointOnStem = PathMerger.FindLinkedPointInPath(stemPath, departureFromLooping.Point);
        const pointsToInsert = Array.from(PathMerger.GetPointsInBetween(departurePointOnStem, arrivalToStem));
        if (PathMerger.Before(departureFromLooping, arrivalToLooping)) {
            this.CleanDisappearedPiece(departureFromLooping, arrivalToLooping, loopingPath);
            this.ReplacePiece(departureFromLooping, arrivalToLooping, pointsToInsert, loopingPath);
        }
        else {
            this.CleanDisappearedPiece(arrivalToLooping, departureFromLooping, loopingPath);
            this.ReplacePiece(arrivalToLooping, departureFromLooping, pointsToInsert.reverse(), loopingPath);
        }
    }
    static *GetPointsInBetween(a, b) {
        for (let i = a.Next; i != b; i = i.Next) {
            yield i.Point;
        }
    }
    ReplacePiece(a, b, points, loopingPath) {
        let prevPoint = a;
        for (const point of points) {
            const lp = new LinkedPoint(point);
            prevPoint.Next = lp;
            prevPoint = lp;
            const pathOffset = this.verticesToPathOffsets.get(point);
            /*Assert.assert(!pathOffset.has(loopingPath))*/
            pathOffset.set(loopingPath, prevPoint);
        }
        prevPoint.Next = b;
    }
    CleanDisappearedPiece(a, b, loopingPath) {
        for (const point of PathMerger.GetPointsInBetween(a, b)) {
            const pathOffset = this.verticesToPathOffsets.get(point);
            /*Assert.assert(pathOffset.has(loopingPath))*/
            pathOffset.delete(loopingPath);
        }
    }
    ///  checks that a is before b of the path
    ///  <param name="a"></param>
    ///  <param name="b"></param>
    ///  <returns>true is a is before b of the path</returns>
    static Before(a, b) {
        for (a = a.Next; a != null; a = a.Next) {
            if (a == b) {
                return true;
            }
        }
        return false;
    }
    static FindLinkedPointInPath(path, point) {
        // this function is supposed to always succeed. it will throw a null reference exception otherwise
        for (let linkedPoint = path.PathPoints;; linkedPoint = linkedPoint.Next) {
            if (linkedPoint.Point.equal(point)) {
                return linkedPoint;
            }
        }
    }
    InitVerticesToPathOffsetsAndRemoveSelfCycles() {
        for (const path of this.Paths) {
            for (let linkedPoint = path.PathPoints; linkedPoint != null; linkedPoint = linkedPoint.Next) {
                let pathOffsets = this.verticesToPathOffsets.get(linkedPoint.Point);
                if (!pathOffsets) {
                    this.verticesToPathOffsets.set(linkedPoint.Point, (pathOffsets = new Map()));
                }
                // check for the loop
                const loopPoint = pathOffsets.get(path);
                if (loopPoint) {
                    // we have a loop
                    this.CleanDisappearedPiece(loopPoint, linkedPoint, path);
                    loopPoint.Next = linkedPoint.Next;
                }
                else {
                    pathOffsets.set(path, linkedPoint);
                }
            }
        }
    }
}
//# sourceMappingURL=PathMerger.js.map