///|
/// Per-window address state defined by RFC 3284 section 5.
priv struct AddressCache {
  near : Array[Int]
  same : Array[Int]
  mut next_near_slot : Int
}

///|
priv struct EncodedAddress {
  mode : Int
  value : Int
  single_byte : Bool
}

///|
fn AddressCache::new() -> AddressCache {
  {
    near: Array::make(DEFAULT_NEAR_CACHE_SIZE, 0),
    same: Array::make(DEFAULT_SAME_CACHE_SIZE * 256, 0),
    next_near_slot: 0,
  }
}

///|
fn AddressCache::update(self : AddressCache, address : Int) -> Unit {
  self.near[self.next_near_slot] = address
  self.next_near_slot = (self.next_near_slot + 1) % self.near.length()
  self.same[address % self.same.length()] = address
}

///|
fn AddressCache::decode(
  self : AddressCache,
  cursor : ByteCursor,
  here : Int,
  mode : Int,
) -> Int raise VcdiffError {
  let offset = cursor.offset()
  let address = if mode == 0 {
    read_varint(cursor)
  } else if mode == 1 {
    here - read_varint(cursor)
  } else if mode >= 2 && mode < 2 + self.near.length() {
    self.near[mode - 2] + read_varint(cursor)
  } else if mode >= 2 + self.near.length() &&
    mode < 2 + self.near.length() + DEFAULT_SAME_CACHE_SIZE {
    let bank = mode - (2 + self.near.length())
    let low = cursor.read_byte().to_int()
    self.same[bank * 256 + low]
  } else {
    raise InvalidAddress(offset~, address=-1, limit=here)
  }
  if address < 0 || address >= here {
    raise InvalidAddress(offset~, address~, limit=here - 1)
  }
  self.update(address)
  address
}

///|
fn AddressCache::encode(
  self : AddressCache,
  address : Int,
  here : Int,
) -> EncodedAddress raise VcdiffError {
  if address < 0 || address >= here {
    raise InvalidAddress(offset=-1, address~, limit=here - 1)
  }
  let mut best_mode = 0
  let mut best_value = address
  let here_value = here - address
  if here_value < best_value {
    best_mode = 1
    best_value = here_value
  }
  for index, near_address in self.near {
    if address >= near_address {
      let near_value = address - near_address
      if near_value < best_value {
        best_mode = index + 2
        best_value = near_value
      }
    }
  }
  let same_slot = address % self.same.length()
  let use_same = self.same[same_slot] == address
  let encoded = if use_same {
    {
      mode: 2 + self.near.length() + same_slot / 256,
      value: same_slot % 256,
      single_byte: true,
    }
  } else {
    { mode: best_mode, value: best_value, single_byte: false }
  }
  self.update(address)
  encoded
}