///|
pub(all) struct EventCursor {
  start_sequence : Int
  limit : Int
  pattern : String
} derive(Eq, @debug.Debug)

///|
pub(all) struct TapeWindow {
  cursor : EventCursor
  entries : Array[TapeEntry]
  next_cursor : EventCursor
  complete : Bool
} derive(Eq, @debug.Debug)

///|
pub fn event_cursor(
  start_sequence? : Int = 0,
  limit? : Int = 100,
  pattern? : StringView = "**",
) -> EventCursor {
  {
    start_sequence: if start_sequence < 0 {
      0
    } else {
      start_sequence
    },
    limit: if limit <= 0 {
      1
    } else {
      limit
    },
    pattern: pattern.to_owned(),
  }
}

///|
pub fn EventCursor::next_from(
  self : EventCursor,
  sequence : Int,
) -> EventCursor {
  { ..self, start_sequence: if sequence < 0 { 0 } else { sequence } }
}

///|
pub fn EventCursor::with_limit(self : EventCursor, limit : Int) -> EventCursor {
  { ..self, limit: if limit <= 0 { 1 } else { limit } }
}

///|
pub fn EventCursor::with_pattern(
  self : EventCursor,
  pattern : StringView,
) -> EventCursor {
  { ..self, pattern: pattern.to_owned() }
}

///|
pub fn EventCursor::to_wire(self : EventCursor) -> String {
  "start=\{self.start_sequence};limit=\{self.limit};pattern=\{escape_wire_text(self.pattern)}"
}

///|
pub fn EventTape::window(
  self : EventTape,
  cursor : EventCursor,
) -> Result[TapeWindow, EventRailError] {
  match topic_pattern(cursor.pattern) {
    Err(err) => Err(err)
    Ok(parsed) => {
      let entries : Array[TapeEntry] = []
      let mut next_sequence = cursor.start_sequence
      let mut complete = true
      for entry in self.entries {
        if entry.sequence < cursor.start_sequence {
          continue
        }
        match parsed.matches_topic(entry.event.topic) {
          Err(err) => return Err(err)
          Ok(false) => ()
          Ok(true) =>
            if entries.length() < cursor.limit {
              entries.push(entry)
              next_sequence = entry.sequence + 1
            } else {
              complete = false
              break
            }
        }
      }
      Ok({
        cursor,
        entries,
        next_cursor: cursor.next_from(next_sequence),
        complete,
      })
    }
  }
}

///|
pub fn TapeWindow::len(self : TapeWindow) -> Int {
  self.entries.length()
}

///|
pub fn TapeWindow::is_empty(self : TapeWindow) -> Bool {
  self.entries.length() == 0
}

///|
pub fn TapeWindow::events(self : TapeWindow) -> Array[Envelope] {
  self.entries.map(entry => entry.event)
}

///|
pub fn TapeWindow::to_tape(self : TapeWindow) -> EventTape {
  let mut tape = event_tape()
  for entry in self.entries {
    tape = tape.append(entry.event)
  }
  tape
}

///|
pub fn TapeWindow::manifest_lines(self : TapeWindow) -> Array[String] {
  let lines : Array[String] = []
  lines.push("cursor=\{self.cursor.to_wire()};complete=\{self.complete}")
  for entry in self.entries {
    lines.push(entry.to_manifest_line())
  }
  lines.push("next=\{self.next_cursor.to_wire()}")
  lines
}

///|
pub fn TapeWindow::manifest(self : TapeWindow) -> String {
  self.manifest_lines().join("\n")
}

///|
pub fn TapeWindow::summary(self : TapeWindow) -> String {
  "window start=\{self.cursor.start_sequence} limit=\{self.cursor.limit} entries=\{self.entries.length()} complete=\{self.complete} next=\{self.next_cursor.start_sequence}"
}

///|
pub fn EventTape::page_count(self : EventTape, limit : Int) -> Int {
  let normalized = if limit <= 0 { 1 } else { limit }
  if self.entries.length() == 0 {
    0
  } else {
    (self.entries.length() + normalized - 1) / normalized
  }
}