///|
/// Creates an empty storage-side fencing guard.
pub fn FenceGuard::new() -> FenceGuard {
  { marks: [] }
}

///|
fn clone_marks(marks : Array[FenceMark]) -> Array[FenceMark] {
  marks.map(fn(mark) { mark })
}

///|
/// Returns the highest accepted token for a resource, or zero if unseen.
pub fn FenceGuard::highest_token(self : FenceGuard, resource : String) -> Int {
  for mark in self.marks {
    if mark.resource == resource {
      return mark.token
    }
  }
  0
}

///|
/// Validates a storage operation against the highest observed fencing token.
pub fn FenceGuard::check(
  self : FenceGuard,
  write : FencedWrite,
) -> FenceDecision {
  if write.resource == "" {
    return {
      next_guard: self,
      accepted: false,
      highest_token: 0,
      message: "resource must not be empty",
    }
  }
  if write.holder == "" {
    return {
      next_guard: self,
      accepted: false,
      highest_token: self.highest_token(write.resource),
      message: "holder must not be empty",
    }
  }
  if write.operation == "" {
    return {
      next_guard: self,
      accepted: false,
      highest_token: self.highest_token(write.resource),
      message: "operation must not be empty",
    }
  }
  if write.token <= 0 {
    return {
      next_guard: self,
      accepted: false,
      highest_token: self.highest_token(write.resource),
      message: "fencing token must be positive",
    }
  }
  let marks = clone_marks(self.marks)
  for index, mark in marks {
    if mark.resource == write.resource {
      if write.token < mark.token {
        return {
          next_guard: self,
          accepted: false,
          highest_token: mark.token,
          message: "stale fencing token rejected",
        }
      }
      if write.token == mark.token && write.holder != mark.holder {
        return {
          next_guard: self,
          accepted: false,
          highest_token: mark.token,
          message: "token is bound to a different holder",
        }
      }
      if write.token > mark.token {
        marks[index] = {
          resource: write.resource,
          token: write.token,
          holder: write.holder,
        }
      }
      return {
        next_guard: { marks, },
        accepted: true,
        highest_token: write.token,
        message: "fenced write accepted",
      }
    }
  }
  marks.push({
    resource: write.resource,
    token: write.token,
    holder: write.holder,
  })
  {
    next_guard: { marks, },
    accepted: true,
    highest_token: write.token,
    message: "fenced write accepted",
  }
}

///|
/// Builds a write request directly from an active lease.
pub fn Lease::fenced_write(
  self : Lease,
  operation : String,
  now : Int,
) -> FencedWrite? {
  if self.is_active(now) {
    Some({
      resource: self.resource,
      holder: self.holder,
      token: self.token,
      operation,
    })
  } else {
    None
  }
}