///| One value held by a multi-value register together with its causal context.

///| Values are strings so this base package stays dependency-free; applications

///|
/// can encode domain records before passing them to the register.
pub(all) struct VersionedValue {
  value : String
  writer : String
  context : VersionVector
} derive(Eq, Debug)

///| A state-based multi-value register. Concurrent writes remain visible while

///|
/// a write that has observed them causally replaces them all.
pub struct MultiValueRegister {
  values : Array[VersionedValue]
} derive(Debug)

///|
/// Validation errors for register writes.
pub(all) enum RegisterError {
  EmptyWriter
} derive(Eq, Debug)

///|
/// Create an empty register.
pub fn MultiValueRegister::new() -> MultiValueRegister {
  { values: [] }
}

///| Inspect current values. More than one item represents an unresolved

///|
/// concurrent update, not an arbitrary last-writer-wins decision.
pub fn MultiValueRegister::values(
  self : MultiValueRegister,
) -> Array[VersionedValue] {
  let output : Array[VersionedValue] = []
  for value in self.values {
    output.push(value)
  }
  output
}

///|
/// Return the only value when the register has converged to one write.
pub fn MultiValueRegister::resolved(
  self : MultiValueRegister,
) -> VersionedValue? {
  if self.values.length() == 1 {
    Some(self.values[0])
  } else {
    None
  }
}

///| Join the contexts represented by currently visible values. A later local

///|
/// write based on this context will dominate all values it has observed.
pub fn MultiValueRegister::context(self : MultiValueRegister) -> VersionVector {
  let mut output = VersionVector::new()
  for value in self.values {
    output = output.merge(value.context)
  }
  output
}

///| Create a causally newer local value. This is a pure operation: persistence

///|
/// and replica transport remain responsibilities of the caller.
pub fn MultiValueRegister::write(
  self : MultiValueRegister,
  writer : String,
  value : String,
) -> Result[MultiValueRegister, RegisterError] {
  if writer.length() == 0 {
    return Err(EmptyWriter)
  }
  match self.context().increment(writer) {
    Ok(context) => Ok(self.apply({ value, writer, context }))
    Err(_) => Err(EmptyWriter)
  }
}

///| Merge a remote register state. Reapplying values is idempotent because

///|
/// equal causal contexts do not create duplicates.
pub fn MultiValueRegister::merge(
  self : MultiValueRegister,
  other : MultiValueRegister,
) -> MultiValueRegister {
  let mut output = self
  for value in other.values {
    output = output.apply(value)
  }
  output
}

///| Apply a remote versioned value. A candidate loses only when an existing

///|
/// value is causally equal or after it; concurrent values are both retained.
pub fn MultiValueRegister::apply(
  self : MultiValueRegister,
  candidate : VersionedValue,
) -> MultiValueRegister {
  let output : Array[VersionedValue] = []
  let mut candidate_is_obsolete = false
  for current in self.values {
    match candidate.context.compare(current.context) {
      Before | Equal => {
        output.push(current)
        candidate_is_obsolete = true
      }
      After => ()
      Concurrent => output.push(current)
    }
  }
  if !candidate_is_obsolete {
    output.push(candidate)
  }
  { values: output }
}

///| Explain whether a register is empty, converged, or needs application-level

///|
/// conflict resolution.
pub(all) enum RegisterState {
  Empty
  Resolved(VersionedValue)
  Conflict(Array[VersionedValue])
} derive(Debug)

///| Convert the register into a rendering-friendly state without forcing an

///|
/// application to inspect array lengths itself.
pub fn MultiValueRegister::state(self : MultiValueRegister) -> RegisterState {
  if self.values.length() == 0 {
    Empty
  } else if self.values.length() == 1 {
    Resolved(self.values[0])
  } else {
    Conflict(self.values)
  }
}