///|
fn DialogCompletionStore::new() -> DialogCompletionStore {
  { pending: [] }
}

///|
fn DialogCompletionStore::register(
  self : DialogCompletionStore,
  dialog : Int64,
) -> PendingDialogCompletion {
  let completion = PendingDialogCompletion::{
    dialog,
    state: Waiting,
    changed: @async.CondVar::Cond(),
  }
  self.pending.push(completion)
  completion
}

///|
fn DialogCompletionStore::remove(
  self : DialogCompletionStore,
  dialog : Int64,
) -> Unit {
  for index, completion in self.pending {
    if completion.dialog == dialog {
      ignore(self.pending.remove(index))
      return
    }
  }
}

///|
fn DialogCompletionStore::complete(
  self : DialogCompletionStore,
  completion : @native.NativeDialogCompletion?,
) -> Unit {
  guard completion is Some(completion) else { return }
  for pending in self.pending {
    if pending.dialog == completion.dialog {
      if pending.state is Waiting {
        pending.state = Completed(completion.result)
        pending.changed.broadcast()
      }
      return
    }
  }
}

///|
async fn DialogCompletionStore::wait(
  self : DialogCompletionStore,
  dialog : Int64,
) -> String {
  let completion = self.register(dialog)
  defer self.remove(dialog)
  while completion.state is Waiting {
    completion.changed.wait()
  }
  match completion.state {
    Completed(Ok(result)) => result
    Completed(Err(error)) => raise error
    Waiting => abort("dialog completion resumed without a result")
  }
}

///|
fn drain_failure_dialog_events(
  runtime : @native.Runtime,
  completions : DialogCompletionStore,
) -> Unit raise @native.NativeError {
  while true {
    match runtime.poll_event() {
      Some(event) => completions.complete(event.dialog_completion())
      None => return
    }
  }
}

///|
async fn wait_for_failure_dialog(
  runtime : @native.Runtime,
  completions : DialogCompletionStore,
  wakeup : RuntimeWakeup,
  dialog : Int64,
) -> Unit raise AppRunError {
  let completion = completions.register(dialog)
  defer completions.remove(dialog)
  while completion.state is Waiting {
    let revision = wakeup.revision()
    drain_failure_dialog_events(runtime, completions) catch {
      error => raise native_run_error("receive dialog completion", error)
    }
    if completion.state is Waiting {
      wakeup.wait_after(revision)
    }
  }
  match completion.state {
    Completed(Ok(_)) => ()
    Completed(Err(error)) =>
      raise native_run_error("complete failure dialog", error)
    Waiting => abort("dialog completion resumed without a result")
  }
}