///|
fn remap_refs(j : Json, ids : Map[Int, Int]) -> Json {
  match j {
    Array(a) => a.map(v => remap_refs(v, ids)).to_json()
    Number(n, ..) => {
      let r = n.to_int()
      let k = ids[arc_id(r)]
      (if r < 0 { -k - 1 } else { k }).to_json()
    }
    _ => j
  }
}

///|
fn remap_geometry(g : Json, ids : Map[Int, Int]) -> Json {
  if kind(g) == "GeometryCollection" {
    replace_fields(
      g,
      Map([
        (
          "geometries",
          array(field(g, "geometries"))
          .map(c => remap_geometry(c, ids))
          .to_json(),
        ),
      ]),
    )
  } else if field(g, "arcs") != Json::null() {
    replace_fields(g, Map([("arcs", remap_refs(field(g, "arcs"), ids))]))
  } else {
    g
  }
}

///|
/// Select objects and remove unused arcs. Signed references are renumbered without changing features.
pub fn Topology::prune(
  self : Topology,
  names : Array[String],
) -> Topology raise TopoError {
  let used : Map[Int, Bool] = Map([])
  for g in self.selected(names) {
    for r in geometry_refs(g) {
      used[arc_id(r)] = true
    }
  }
  let old_ids = used.keys().collect()
  old_ids.sort()
  let mapping : Map[Int, Int] = Map([])
  let old_arcs = array(field(self.data, "arcs"))
  let arcs = []
  for k = 0; k < old_ids.length(); k = k + 1 {
    mapping[old_ids[k]] = k
    arcs.push(old_arcs[old_ids[k]])
  }
  let objects : Map[String, Json] = Map([])
  for name in names {
    objects[name] = remap_geometry(self.object(name), mapping)
  }
  let out = match self.data {
    Object(m) => m.copy()
    _ => Map([])
  }
  out.remove("bbox")
  out["arcs"] = arcs.to_json()
  out["objects"] = Json::object(objects)
  let t = read(Json::object(out).stringify())
  match t.bbox() {
    Some(b) =>
      read(replace_fields(t.data, Map([("bbox", b.to_json())])).stringify())
    None => t
  }
}