"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

var _Feature = require("../Core/Feature");

function pointIsOverLine(point, linePoints, epsilon, offset, count, size) {
  var x0 = point.x;
  var y0 = point.y; // for each segment of the line (j is i -1)

  for (var i = offset + size, j = offset; i < offset + count; j = i, i += size) {
    /* **********************************************************
        norm     : norm of vector P1P2
        distance : distance point P0 to line P1P2
        scalar   : dot product of P1P0 and P1P2 divide by norm, it represents the projection of P0 on the line
         Point is over segment P1P2 if :
            * if the distance, , is inferior to epsilon
            * and if :  -epsilon ≤ scalar ≤ (||P1P2|| +  epsilon)
                         + (P0) _
                        |      |
                        |      |
         <---scalar---->|    distance
                        |      |
                        |      v
         +-------------------------------+
        (P1)                            (P2)
    *********************************************************** */
    var x1 = linePoints[i];
    var y1 = linePoints[i + 1];
    var x2 = linePoints[j];
    var y2 = linePoints[j + 1];
    var x21 = x2 - x1;
    var y21 = y2 - y1;
    var norm = Math.sqrt(x21 * x21 + y21 * y21);
    var scalar = ((x0 - x1) * x21 + (y0 - y1) * y21) / norm;

    if (scalar >= -epsilon && scalar <= norm + epsilon) {
      var distance = Math.abs(y21 * x0 - x21 * y0 + x2 * y1 - y2 * x1) / norm;

      if (distance <= epsilon) {
        return true;
      }
    }
  }

  return false;
}

function getClosestPoint(point, points, epsilon, offset, count, size) {
  var x0 = point.x;
  var y0 = point.y;
  var squaredEpsilon = epsilon * epsilon;
  var closestPoint;

  for (var i = offset; i < offset + count; i += size) {
    var x1 = points[i];
    var y1 = points[i + 1];
    var xP = x0 - x1;
    var yP = y0 - y1;
    var n = xP * xP + yP * yP;

    if (n < squaredEpsilon) {
      closestPoint = [points[i], points[i + 1]];
      squaredEpsilon = n;
    }
  }

  return closestPoint;
}

function pointIsInsidePolygon(point, polygonPoints, offset, count, size) {
  // ray-casting algorithm based on
  // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
  var x = point.x;
  var y = point.y;
  var inside = false; // in first j is last point of polygon
  // for each segment of the polygon (j is i -1)
  // debugger;

  for (var i = offset, j = offset + count - size; i < offset + count; j = i, i += size) {
    var xi = polygonPoints[i];
    var yi = polygonPoints[i + 1];
    var xj = polygonPoints[j];
    var yj = polygonPoints[j + 1]; // isIntersect semi-infinite ray horizontally with polygon's edge

    if (yi > y != yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
      inside = !inside;
    }
  }

  return inside;
}

function isFeatureSingleGeometryUnderCoordinate(coordinate, type, coordinates, epsilon, offset, count, size) {
  if (type == _Feature.FEATURE_TYPES.LINE && pointIsOverLine(coordinate, coordinates, epsilon, offset, count, size)) {
    return true;
  } else if (type == _Feature.FEATURE_TYPES.POLYGON && pointIsInsidePolygon(coordinate, coordinates, offset, count, size)) {
    return true;
  } else if (type == _Feature.FEATURE_TYPES.POINT) {
    var closestPoint = getClosestPoint(coordinate, coordinates, epsilon, offset, count, size);

    if (closestPoint) {
      return {
        coordinates: closestPoint
      };
    }
  }
}

function isFeatureUnderCoordinate(coordinate, feature, epsilon, result) {
  var featCoord = coordinate.as(feature.crs);
  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = feature.geometry[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var geometry = _step.value;

      if (geometry.extent == undefined || geometry.extent.isPointInside(featCoord, epsilon)) {
        var offset = geometry.indices[0].offset * feature.size;
        var count = geometry.indices[0].count * feature.size;
        var under = isFeatureSingleGeometryUnderCoordinate(featCoord, feature.type, feature.vertices, epsilon, offset, count, feature.size);

        if (under) {
          result.push({
            type: feature.type,
            geometry: geometry,
            coordinates: under.coordinates
            /* || coordinates */

          });
        }
      }
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator["return"] != null) {
        _iterator["return"]();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }
}

var _default = {
  /**
   * Filter from a list of features, features that are under a coordinate.
   *
   * @param {Coordinates} coordinate - The coordinate for the filter
   * condition.
   * @param {Feature|FeatureCollection} features - A single feature or a
   * collection of them, to filter given the previous coordinate.
   * @param {number} [epsilon=0.1] Tolerance around the coordinate (in
   * coordinate's unit).
   *
   * @return {Feature[]} Array of filtered features.
   */
  filterFeaturesUnderCoordinate: function filterFeaturesUnderCoordinate(coordinate, features) {
    var epsilon = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.1;
    var result = []; // We can take this shortcut because either Feature and
    // FeatureCollection have an extent property

    if (features.extent && !features.extent.isPointInside(coordinate, epsilon)) {
      return result;
    }

    if (Array.isArray(features.features)) {
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = features.features[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var feature = _step2.value;

          if (feature.extent && !feature.extent.isPointInside(coordinate, epsilon)) {
            continue;
          }

          isFeatureUnderCoordinate(coordinate, feature, epsilon, result);
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
            _iterator2["return"]();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }
    } else if (features.geometry) {
      isFeatureUnderCoordinate(coordinate, features, epsilon, result);
    }

    return result;
  }
};
exports["default"] = _default;