text: /**
   * @module ol/geom/flat/orient
   */
  import {coordinates as reverseCoordinates} from './reverse.js';
  
  /**
   * Is the linear ring oriented clockwise in a coordinate system with a bottom-left
   * coordinate origin? For a coordinate system with a top-left coordinate origin,
   * the ring's orientation is clockwise when this function returns false.
   * @param {Array<number>} flatCoordinates Flat coordinates.
   * @param {number} offset Offset.
   * @param {number} end End.
   * @param {number} stride Stride.
   * @return {boolean} Is clockwise.
   */
  export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
    // https://stackoverflow.com/q/1165647/clockwise-method#1165943
    // https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrlinearring.cpp
    let edge = 0;
    let x1 = flatCoordinates[end - stride];
    let y1 = flatCoordinates[end - stride + 1];
    for (; offset < end; offset += stride) {
      const x2 = flatCoordinates[offset];
      const y2 = flatCoordinates[offset + 1];
      edge += (x2 - x1) * (y2 + y1);
      x1 = x2;
      y1 = y2;
    }
    return edge === 0 ? undefined : edge > 0;
  }
  
  /**
   * Determines if linear rings are oriented.  By default, left-hand orientation
   * is tested (first ring must be clockwise, remaining rings counter-clockwise).
   * To test for right-hand orientation, use the `opt_right` argument.
   *
   * @param {Array<number>} flatCoordinates Flat coordinates.
   * @param {number} offset Offset.
   * @param {Array<number>} ends Array of end indexes.
   * @param {number} stride Stride.
   * @param {boolean} [opt_right] Test for right-hand orientation
   *     (counter-clockwise exterior ring and clockwise interior rings).
   * @return {boolean} Rings are correctly oriented.
   */
  export function linearRingsAreOriented(
    flatCoordinates,
    offset,
    ends,
    stride,
    opt_right
  ) {
    const right = opt_right !== undefined ? opt_right : false;
    for (let i = 0, ii = ends.length; i < ii; ++i) {
      const end = ends[i];
      const isClockwise = linearRingIsClockwise(
        flatCoordinates,
        offset,
        end,
        stride
      );
      if (i === 0) {
        if ((right && isClockwise) || (!right && !isClockwise)) {
          return false;
        }
      } else {
        if ((right && !isClockwise) || (!right && isClockwise)) {
          return false;
        }
      }
      offset = end;
    }
    return true;
  }
  
  /**
   * Determines if linear rings are oriented.  By default, left-hand orientation
   * is tested (first ring must be clockwise, remaining rings counter-clockwise).
   * To test for right-hand orientation, use the `opt_right` argument.
   *
   * @param {Array<number>} flatCoordinates Flat coordinates.
   * @param {number} offset Offset.
   * @param {Array<Array<number>>} endss Array of array of end indexes.
   * @param {number} stride Stride.
   * @param {boolean} [opt_right] Test for right-hand orientation
   *     (counter-clockwise exterior ring and clockwise interior rings).
   * @return {boolean} Rings are correctly oriented.
   */
  export function linearRingssAreOriented(
    flatCoordinates,
    offset,
    endss,
    stride,
    opt_right
  ) {
    for (let i = 0, ii = endss.length; i < ii; ++i) {
      const ends = endss[i];
      if (
        !linearRingsAreOriented(flatCoordinates, offset, ends, stride, opt_right)
      ) {
        return false;
      }
      if (ends.length) {
        offset = ends[ends.length - 1];
      }
    }
    return true;
  }
  
  /**
   * Orient coordinates in a flat array of linear rings.  By default, rings
   * are oriented following the left-hand rule (clockwise for exterior and
   * counter-clockwise for interior rings).  To orient according to the
   * right-hand rule, use the `opt_right` argument.
   *
   * @param {Array<number>} flatCoordinates Flat coordinates.
   * @param {number} offset Offset.
   * @param {Array<number>} ends Ends.
   * @param {number} stride Stride.
   * @param {boolean} [opt_right] Follow the right-hand rule for orientation.
   * @return {number} End.
   */
  export function orientLinearRings(
    flatCoordinates,
    offset,
    ends,
    stride,
    opt_right
  ) {
    const right = opt_right !== undefined ? opt_right : false;
    for (let i = 0, ii = ends.length; i < ii; ++i) {
      const end = ends[i];
      const isClockwise = linearRingIsClockwise(
        flatCoordinates,
        offset,
        end,
        stride
      );
      const reverse =
        i === 0
          ? (right && isClockwise) || (!right && !isClockwise)
          : (right && !isClockwise) || (!right && isClockwise);
      if (reverse) {
        reverseCoordinates(flatCoordinates, offset, end, stride);
      }
      offset = end;
    }
    return offset;
  }
  
  /**
   * Orient coordinates in a flat array of linear rings.  By default, rings
   * are oriented following the left-hand rule (clockwise for exterior and
   * counter-clockwise for interior rings).  To orient according to the
   * right-hand rule, use the `opt_right` argument.
   *
   * @param {Array<number>} flatCoordinates Flat coordinates.
   * @param {number} offset Offset.
   * @param {Array<Array<number>>} endss Array of array of end indexes.
   * @param {number} stride Stride.
   * @param {boolean} [opt_right] Follow the right-hand rule for orientation.
   * @return {number} End.
   */
  export function orientLinearRingsArray(
    flatCoordinates,
    offset,
    endss,
    stride,
    opt_right
  ) {
    for (let i = 0, ii = endss.length; i < ii; ++i) {
      offset = orientLinearRings(
        flatCoordinates,
        offset,
        endss[i],
        stride,
        opt_right
      );
    }
    return offset;
  }
  
  /**
   * Return a two-dimensional endss
   * @param {Array<number>} flatCoordinates Flat coordinates
   * @param {Array<number>} ends Linear ring end indexes
   * @return {Array<Array<number>>} Two dimensional endss array that can
   * be used to contruct a MultiPolygon
   */
  export function inflateEnds(flatCoordinates, ends) {
    const endss = [];
    let offset = 0;
    let prevEndIndex = 0;
    for (let i = 0, ii = ends.length; i < ii; ++i) {
      const end = ends[i];
      // classifies an array of rings into polygons with outer rings and holes
      if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
        endss.push(ends.slice(prevEndIndex, i + 1));
      } else {
        if (endss.length === 0) {
          continue;
        }
        endss[endss.length - 1].push(ends[prevEndIndex]);
      }
      prevEndIndex = i + 1;
      offset = end;
    }
    return endss;
  }
  