///|
/// A source order is an explicit output preference, not a merge of row moves.
/// Surviving rows missing from that source are appended in lexical key order.
pub(all) enum RowOrder {
  Lexical
  BaseOrder
  OursOrder
  TheirsOrder
} derive(Eq, Debug)

///|
pub extend RowOrder with Eq::{equal, not_equal}

///|
pub extend RowOrder with @debug.Debug::{to_repr}

///|
pub fn RowOrder::parse(value : String) -> RowOrder raise MergeError {
  match value {
    "lexical" => Lexical
    "base" => BaseOrder
    "ours" => OursOrder
    "theirs" => TheirsOrder
    _ => raise InvalidOrder("use lexical/base/ours/theirs; got '\{value}'")
  }
}

///|
pub fn RowOrder::name(self : RowOrder) -> String {
  match self {
    Lexical => "lexical"
    BaseOrder => "base"
    OursOrder => "ours"
    TheirsOrder => "theirs"
  }
}

///|
pub fn MergePlan::row_order(self : MergePlan) -> RowOrder {
  self.ordering
}

///|
fn order_rows(
  ids : Array[(String, RowKey)],
  ordering : RowOrder,
  base : Indexed,
  ours : Indexed,
  theirs : Indexed,
) -> Unit {
  let source = match ordering {
    Lexical => return
    BaseOrder => base
    OursOrder => ours
    TheirsOrder => theirs
  }
  // The identity map is populated in physical row order by index_table.
  let positions : Map[String, Int] = Map([])
  for i, (token, _) in source.identities.to_array() {
    positions[token] = i
  }
  ids.sort_by((a, b) => {
    match (positions.get(a.0), positions.get(b.0)) {
      (Some(x), Some(y)) => x.compare(y)
      (Some(_), None) => -1
      (None, Some(_)) => 1
      (None, None) => key_compare(a.1, b.1)
    }
  })
}