///|
/// Local rich-text document with reversible changes. History stores inverse
/// Deltas, so edits to embedded values have the same restrictions as invert.
pub struct History {
  mut document : Delta
  undo_stack : Array[Delta]
  mut redo_stack : Array[Delta]
  mut revision : Int32
  max_depth : Int32
}

///|
pub fn History::new(
  document : Delta,
  max_depth : Int32,
) -> History raise DeltaError {
  if !document.is_document() {
    raise Invalid("history requires an insert-only document")
  }
  if max_depth <= 0 {
    raise Invalid("history depth must be positive")
  }
  {
    document: Delta::from_ops(document.operations()),
    undo_stack: [],
    redo_stack: [],
    revision: 0,
    max_depth,
  }
}

///|
pub fn History::document(self : History) -> Delta {
  Delta::from_ops(self.document.operations())
}

///|
pub fn History::revision(self : History) -> Int32 {
  self.revision
}

///|
pub fn History::can_undo(self : History) -> Bool {
  !self.undo_stack.is_empty()
}

///|
pub fn History::can_redo(self : History) -> Bool {
  !self.redo_stack.is_empty()
}

///|
pub fn History::undo_depth(self : History) -> Int32 {
  self.undo_stack.length()
}

///|
pub fn History::redo_depth(self : History) -> Int32 {
  self.redo_stack.length()
}

///|
pub fn History::apply(self : History, change : Delta) -> Unit raise DeltaError {
  let inverse = change.invert(self.document)
  let next = self.document.apply(change)
  self.document = next
  self.undo_stack.push(inverse)
  if self.undo_stack.length() > self.max_depth {
    ignore(self.undo_stack.remove(0))
  }
  self.redo_stack = []
  self.revision += 1
}

///|
/// Undo the most recent local edit. Returns false when there is nothing to do.
pub fn History::undo(self : History) -> Bool raise DeltaError {
  let inverse = match self.undo_stack.pop() {
    Some(change) => change
    None => return false
  }
  let redo = inverse.invert(self.document)
  self.document = self.document.apply(inverse)
  self.redo_stack.push(redo)
  self.revision += 1
  true
}

///|
/// Redo the most recently undone edit. Returns false when there is none.
pub fn History::redo(self : History) -> Bool raise DeltaError {
  let change = match self.redo_stack.pop() {
    Some(change) => change
    None => return false
  }
  let inverse = change.invert(self.document)
  self.document = self.document.apply(change)
  self.undo_stack.push(inverse)
  if self.undo_stack.length() > self.max_depth {
    ignore(self.undo_stack.remove(0))
  }
  self.revision += 1
  true
}