// Ported from topojson-client src/mesh.js, ISC.

///|
pub(all) enum Boundary {
  All
  Interior
  Exterior
} derive(Eq, Debug)

///|
fn collect_leaves(g : Json, out : Array[Json]) -> Unit {
  if kind(g) == "GeometryCollection" {
    for c in array(field(g, "geometries")) {
      collect_leaves(c, out)
    }
  } else {
    out.push(g)
  }
}

///|
fn mesh_ids(g : Json, mode : Boundary) -> Array[Int] {
  let leaves : Array[Json] = []
  collect_leaves(g, leaves)
  let owners : Map[Int, Array[(Int, Int)]] = Map([])
  for k = 0; k < leaves.length(); k = k + 1 {
    for r in geometry_refs(leaves[k]) {
      let id = arc_id(r)
      let a = owners.get(id).unwrap_or([])
      a.push((r, k))
      owners[id] = a
    }
  }
  let keys = owners.keys().collect()
  keys.sort()
  let out = []
  for id in keys {
    let a = owners[id]
    let shared = a[0].1 != a[a.length() - 1].1
    if mode == All ||
      (mode == Interior && shared) ||
      (mode == Exterior && !shared) {
      out.push(a[0].0)
    }
  }
  out
}

///|
/// Return a MultiLineString with arc references. With no object, selects all stored arcs.
pub fn Topology::mesh_arcs(
  self : Topology,
  name? : String,
  boundary? : Boundary = All,
) -> Json raise TopoError {
  let ids = match name {
    Some(n) => mesh_ids(self.lookup(n), boundary)
    None => {
      if boundary != All {
        raise Invalid("mesh.object_required")
      }
      Array::makei(array(field(self.data, "arcs")).length(), i => i)
    }
  }
  { "type": "MultiLineString", "arcs": self.stitch_refs(ids).to_json() }
}

///|
pub fn Topology::mesh(
  self : Topology,
  name? : String,
  boundary? : Boundary = All,
) -> Json raise TopoError {
  self.geometry(self.mesh_arcs(name?, boundary~))
}