// Ported from georust/geo
// https://github.com/georust/geo/blob/main/geo/src/algorithm/line_interpolate_point.rs

///|
/// Returns the point along `line` at the given `fraction` of its length
/// (0.0 = start, 1.0 = end). Values outside [0, 1] are clamped.
pub fn line_interpolate_point(
  line : @type.Line,
  fraction : Double,
) -> @type.Point {
  let f = if fraction < 0.0 {
    0.0
  } else if fraction > 1.0 {
    1.0
  } else {
    fraction
  }
  @type.Point::from_coord(line.start() + line.delta().mul(f))
}

///|
/// Returns the point along `linestring` at the given `fraction` of its total
/// length. Returns `None` for empty / single-coord linestrings.
pub fn line_string_interpolate_point(
  ls : @type.LineString,
  fraction : Double,
) -> @type.Point? {
  let segments = ls.lines()
  if segments.length() == 0 {
    return None
  }
  let total_length = euclidean_length_line_string(ls)
  if total_length == 0.0 {
    return None
  }
  let f = if fraction < 0.0 {
    0.0
  } else if fraction > 1.0 {
    1.0
  } else {
    fraction
  }
  let target = f * total_length
  // cumulative[i] = euclidean length of segments[0..=i].
  let cumulative = segments
    .iter()
    .fold(init=([] : Array[Double]), fn(acc, seg) {
      let prev = if acc.length() > 0 { acc[acc.length() - 1] } else { 0.0 }
      [..acc, prev + euclidean_length_line(seg)]
    })
  match
    (0).until(segments.length()).find_first(fn(i) { cumulative[i] >= target }) {
    Some(i) => {
      let seg = segments[i]
      let seg_length = euclidean_length_line(seg)
      let prev_acc = if i == 0 { 0.0 } else { cumulative[i - 1] }
      let local_fraction = if seg_length == 0.0 {
        0.0
      } else {
        (target - prev_acc) / seg_length
      }
      Some(line_interpolate_point(seg, local_fraction))
    }
    None => Some(@type.Point::from_coord(segments[segments.length() - 1].end()))
  }
}