///|
/// A named attribution value attached to CRDT identities.
pub(all) struct ContentAttribute[A] {
  name : String
  value : A
} derive(Eq, Debug)

///|
pub fn[A] ContentAttribute::ContentAttribute(
  name : String,
  value : A,
) -> ContentAttribute[A] {
  { name, value, }
}

///|
/// Attribute equality is independent of order, as in Yrs.
pub struct ContentAttributes[A] {
  priv values : ReadOnlyArray[ContentAttribute[A]]
} derive(Debug)

///|
pub fn[A] ContentAttributes::ContentAttributes(
  values~ : Array[ContentAttribute[A]] = [],
) -> ContentAttributes[A] {
  { values: ReadOnlyArray::from_array(values[:]), }
}

///|
pub fn[A] ContentAttributes::as_array(
  self : ContentAttributes[A],
) -> Array[ContentAttribute[A]] {
  self.values[:].to_owned()
}

///|
pub fn[A] ContentAttributes::length(self : ContentAttributes[A]) -> Int32 {
  self.values.length()
}

///|
pub impl[A : Eq] Eq for ContentAttributes[A] with fn equal(self, other) {
  self.values.length() == other.values.length() &&
  self.values.all(fn(a) { other.values.contains(a) })
}

///|
pub fn[A : Eq] ContentAttributes::merge(
  self : ContentAttributes[A],
  other : ContentAttributes[A],
) -> ContentAttributes[A] {
  let values = self.as_array()
  for attr in other.values {
    if !values.contains(attr) {
      values.push(attr)
    }
  }
  ContentAttributes(values~)
}

///|
/// An attributed half-open clock interval. The client is carried by IdMap.
pub(all) struct AttrRange[A] {
  start : UInt32
  end : UInt32
  attrs : ContentAttributes[A]
} derive(Eq, Debug)

///|
pub fn[A] AttrRange::AttrRange(
  start : UInt32,
  end : UInt32,
  attrs~ : ContentAttributes[A] = ContentAttributes(),
) -> AttrRange[A] {
  guard start <= end else { abort("reversed attribution range") }
  { start, end, attrs, }
}

///|
/// A contiguous interval of identities from one client.
pub(all) struct BlockRange {
  client : UInt64
  clock : UInt32
  length : UInt32
} derive(Eq, Debug)

///|
pub fn BlockRange::BlockRange(
  client : UInt64,
  clock : UInt32,
  length : UInt32,
) -> BlockRange {
  guard length <= 0xffff_ffffU - clock else { abort("clock range overflow") }
  { client, clock, length, }
}

///|
fn BlockRange::end(self : BlockRange) -> UInt32 {
  guard self.length <= 0xffff_ffffU - self.clock else {
    abort("clock range overflow")
  }
  self.clock + self.length
}

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

///|
pub extend ContentAttribute with Debug::{to_repr}

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

///|
pub extend ContentAttributes with Debug::{to_repr}

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

///|
pub extend AttrRange with Debug::{to_repr}

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

///|
pub extend BlockRange with Debug::{to_repr}