import bearing from "@turf/bearing";
import distance from "@turf/distance";
import destination from "@turf/destination";
import lineIntersects from "@turf/line-intersect";
import { flattenEach } from "@turf/meta";
import { point, lineString, } from "@turf/helpers";
import { getCoords } from "@turf/invariant";
/**
 * Takes a {@link Point} and a {@link LineString} and calculates the closest Point on the (Multi)LineString.
 *
 * @name nearestPointOnLine
 * @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
 * @param {Geometry|Feature<Point>|number[]} pt point to snap from
 * @param {Object} [options={}] Optional parameters
 * @param {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers
 * @returns {Feature<Point>} closest point on the `line` to `point`. The properties object will contain three values: `index`: closest point was found on nth line part, `dist`: distance between pt and the closest point, `location`: distance along the line between start and the closest point.
 * @example
 * var line = turf.lineString([
 *     [-77.031669, 38.878605],
 *     [-77.029609, 38.881946],
 *     [-77.020339, 38.884084],
 *     [-77.025661, 38.885821],
 *     [-77.021884, 38.889563],
 *     [-77.019824, 38.892368]
 * ]);
 * var pt = turf.point([-77.037076, 38.884017]);
 *
 * var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
 *
 * //addToMap
 * var addToMap = [line, pt, snapped];
 * snapped.properties['marker-color'] = '#00f';
 */
function nearestPointOnLine(lines, pt, options) {
    if (options === void 0) { options = {}; }
    var closestPt = point([Infinity, Infinity], {
        dist: Infinity,
    });
    var length = 0.0;
    flattenEach(lines, function (line) {
        var coords = getCoords(line);
        for (var i = 0; i < coords.length - 1; i++) {
            //start
            var start = point(coords[i]);
            start.properties.dist = distance(pt, start, options);
            //stop
            var stop_1 = point(coords[i + 1]);
            stop_1.properties.dist = distance(pt, stop_1, options);
            // sectionLength
            var sectionLength = distance(start, stop_1, options);
            //perpendicular
            var heightDistance = Math.max(start.properties.dist, stop_1.properties.dist);
            var direction = bearing(start, stop_1);
            var perpendicularPt1 = destination(pt, heightDistance, direction + 90, options);
            var perpendicularPt2 = destination(pt, heightDistance, direction - 90, options);
            var intersect = lineIntersects(lineString([
                perpendicularPt1.geometry.coordinates,
                perpendicularPt2.geometry.coordinates,
            ]), lineString([start.geometry.coordinates, stop_1.geometry.coordinates]));
            var intersectPt = null;
            if (intersect.features.length > 0) {
                intersectPt = intersect.features[0];
                intersectPt.properties.dist = distance(pt, intersectPt, options);
                intersectPt.properties.location =
                    length + distance(start, intersectPt, options);
            }
            if (start.properties.dist < closestPt.properties.dist) {
                closestPt = start;
                closestPt.properties.index = i;
                closestPt.properties.location = length;
            }
            if (stop_1.properties.dist < closestPt.properties.dist) {
                closestPt = stop_1;
                closestPt.properties.index = i + 1;
                closestPt.properties.location = length + sectionLength;
            }
            if (intersectPt &&
                intersectPt.properties.dist < closestPt.properties.dist) {
                closestPt = intersectPt;
                closestPt.properties.index = i;
            }
            // update length
            length += sectionLength;
        }
    });
    return closestPt;
}
export default nearestPointOnLine;
