///|
pub struct FetchSlot {
  url : String
  earliest_tick : Int
} derive(Debug, Eq)

///|
pub fn schedule_fetches(
  text : String,
  agent : String,
  urls : Array[String],
  start_tick : Int,
) -> Array[FetchSlot] {
  let delay = crawl_delay_or(text, agent, 1)
  let allowed = next_batch(text, agent, urls, urls.length())
  let slots : Array[FetchSlot] = []
  let mut tick = start_tick
  for url in allowed {
    slots.push({ url, earliest_tick: tick })
    tick += delay
  }
  slots
}

///|
pub fn schedule_report(slots : Array[FetchSlot]) -> String {
  let lines : Array[String] = []
  for slot in slots {
    lines.push(slot.earliest_tick.to_string() + " " + slot.url)
  }
  join_lines(lines)
}

///|
pub fn next_allowed_tick(
  text : String,
  agent : String,
  previous_tick : Int,
) -> Int {
  previous_tick + crawl_delay_or(text, agent, 1)
}

///|
pub fn is_due(now : Int, slot : FetchSlot) -> Bool {
  now >= slot.earliest_tick
}

///|
pub fn due_urls(now : Int, slots : Array[FetchSlot]) -> Array[String] {
  let urls : Array[String] = []
  for slot in slots {
    if is_due(now, slot) {
      urls.push(slot.url)
    }
  }
  urls
}

///|
pub fn pending_urls(now : Int, slots : Array[FetchSlot]) -> Array[String] {
  let urls : Array[String] = []
  for slot in slots {
    if !is_due(now, slot) {
      urls.push(slot.url)
    }
  }
  urls
}