///|
pub(all) struct VectorClock {
  entries : Array[ClockEntry]
}

///|
pub fn VectorClock::new() -> VectorClock {
  { entries: [] }
}

///|
pub fn VectorClock::from_entries(entries : Array[ClockEntry]) -> VectorClock {
  let mut clock = VectorClock::new()
  for entry in entries {
    clock = clock.set(entry.node, entry.counter)
  }
  clock
}

///|
pub fn VectorClock::get(self : VectorClock, node : String) -> Int {
  for entry in self.entries {
    if entry.node == node {
      return entry.counter
    }
  }
  0
}

///|
pub fn VectorClock::set(
  self : VectorClock,
  node : String,
  counter : Int,
) -> VectorClock {
  let next = []
  let mut found = false
  for entry in self.entries {
    if entry.node == node {
      next.push(ClockEntry::new(node, counter))
      found = true
    } else {
      next.push(entry)
    }
  }
  if !found {
    next.push(ClockEntry::new(node, counter))
  }
  { entries: next }
}

///|
pub fn VectorClock::tick(self : VectorClock, node : String) -> VectorClock {
  self.set(node, self.get(node) + 1)
}

///|
pub fn VectorClock::merge(
  self : VectorClock,
  other : VectorClock,
) -> VectorClock {
  let mut merged = self
  for entry in other.entries {
    let local_counter = merged.get(entry.node)
    if entry.counter > local_counter {
      merged = merged.set(entry.node, entry.counter)
    }
  }
  merged
}

///|
fn node_seen(nodes : Array[String], node : String) -> Bool {
  for item in nodes {
    if item == node {
      return true
    }
  }
  false
}

///|
fn all_nodes(left : VectorClock, right : VectorClock) -> Array[String] {
  let nodes = []
  for entry in left.entries {
    if !node_seen(nodes, entry.node) {
      nodes.push(entry.node)
    }
  }
  for entry in right.entries {
    if !node_seen(nodes, entry.node) {
      nodes.push(entry.node)
    }
  }
  nodes
}

///|
pub fn VectorClock::compare(
  self : VectorClock,
  other : VectorClock,
) -> CausalOrder {
  let mut less = false
  let mut greater = false
  for node in all_nodes(self, other) {
    let left = self.get(node)
    let right = other.get(node)
    if left < right {
      less = true
    } else if left > right {
      greater = true
    }
  }
  if less && greater {
    Concurrent
  } else if less {
    Before
  } else if greater {
    After
  } else {
    Equal
  }
}

///|
pub fn VectorClock::happens_before(
  self : VectorClock,
  other : VectorClock,
) -> Bool {
  self.compare(other) == Before
}

///|
pub fn VectorClock::concurrent_with(
  self : VectorClock,
  other : VectorClock,
) -> Bool {
  self.compare(other) == Concurrent
}