/// ================================================================================
/// CAPTURE DIFF HOOK ALGORITHM
/// ================================================================================
///
/// The Capture algorithm is a simple but essential component that collects
/// all diff operations as they are generated by other algorithms.
///
/// ALGORITHM OVERVIEW:
/// 1. Collects all operations (Equal, Delete, Insert, Replace) in sequence
/// 2. Provides methods to access collected operations
/// 3. Sorts operations for consistent ordering
/// 4. Acts as a foundation for testing and debugging diff algorithms
///
/// DESIGN PATTERN: Observer pattern - captures operations without processing
/// TIME COMPLEXITY: O(1) per operation, O(N log N) for final sorting
/// SPACE COMPLEXITY: O(N) where N is the number of operations

///| ================================================================================
/// CAPTURE STRUCTURE
/// ================================================================================
///
/// A simple wrapper around Array[DiffOp] that implements the DiffHook interface
/// This allows it to collect operations from any diff algorithm
type Capture Array[DiffOp] derive(Default)

/// ================================================================================
/// CONSTRUCTION METHODS
/// ================================================================================

///| Creates a new empty Capture hook
///
/// @return New Capture instance ready to collect operations
pub fn Capture::new() -> Capture {
  Capture::default()
}

///| Consumes the Capture hook and returns the collected operations
///
/// @return Array of all captured diff operations
pub fn Capture::into_ops(self : Capture) -> Array[DiffOp] {
  self.inner()
}

///| Returns an iterator over the captured operations
///
/// @return Iterator yielding each captured operation in order
pub fn Capture::to_iter(self : Capture) -> Iter[DiffOp] {
  self.inner().iter()
}

///| Returns a reference to the captured operations without consuming
///
/// @return Reference to the underlying operations array
pub fn Capture::ops(self : Capture) -> Array[DiffOp] {
  self.inner()
}

/// ================================================================================
/// DIFF HOOK IMPLEMENTATION
/// ================================================================================
///
/// These methods implement the DiffHook interface to collect operations
/// Each method simply appends the operation to the internal array

///| Captures an Equal operation indicating unchanged content
///
/// @param old_index Starting index in old sequence
/// @param new_index Starting index in new sequence  
/// @param len Number of equal elements
pub impl DiffHook for Capture with equal(
  self : Capture,
  old_index : Int,
  new_index : Int,
  len : Int,
) -> Result[Unit, Error] {
  self.inner().push(DiffOp::Equal(old_index~, new_index~, len~))
  Ok(())
}

///| Captures a Delete operation indicating removed content
///
/// @param old_index Starting index in old sequence
/// @param old_len Number of deleted elements
/// @param new_index Position in new sequence where deletion occurred
pub impl DiffHook for Capture with delete(
  self : Capture,
  old_index : Int,
  old_len : Int,
  new_index : Int,
) -> Result[Unit, Error] {
  self.inner().push(DiffOp::Delete(old_index~, old_len~, new_index~))
  Ok(())
}

///| Captures an Insert operation indicating added content
///
/// @param old_index Position in old sequence where insertion occurred
/// @param new_index Starting index in new sequence
/// @param new_len Number of inserted elements
pub impl DiffHook for Capture with insert(
  self : Capture,
  old_index : Int,
  new_index : Int,
  new_len : Int,
) -> Result[Unit, Error] {
  self.inner().push(DiffOp::Insert(old_index~, new_index~, new_len~))
  Ok(())
}

///| Captures a Replace operation indicating changed content
///
/// @param old_index Starting index in old sequence
/// @param old_len Number of elements replaced
/// @param new_index Starting index in new sequence
/// @param new_len Number of replacement elements
pub impl DiffHook for Capture with replace(
  self : Capture,
  old_index : Int,
  old_len : Int,
  new_index : Int,
  new_len : Int,
) -> Result[Unit, Error] {
  self.inner().push(DiffOp::Replace(old_index~, old_len~, new_index~, new_len~))
  Ok(())
}

///| Final processing step - sorts operations for consistent output
///
/// ALGORITHM: Sorts operations by old sequence position, then new sequence position
/// This ensures deterministic output regardless of algorithm execution order
pub impl DiffHook for Capture with finish(self : Capture) -> Result[Unit, Error] {
  self.sort()
  Ok(())
}

///| ================================================================================
/// SORTING ALGORITHM
/// ================================================================================
///
/// Sorts captured operations to ensure consistent and predictable output
///
/// SORTING STRATEGY:
/// 1. Primary sort key: position in old sequence (ascending)
/// 2. Secondary sort key: position in new sequence (ascending)
/// 3. Tertiary rule: Delete operations come before other operations at same position
///
/// This ordering produces diffs that read naturally from top to bottom
pub fn Capture::sort(self : Capture) -> Unit {
  /// Helper function to extract sorting keys from operations
  fn get_old_new_index(op : DiffOp) -> (Int, Int)? {
    match op {
      DiffOp::Equal(old_index~, new_index~, len=_) =>
        Some((old_index, new_index))
      DiffOp::Delete(old_index~, old_len=_, new_index~) =>
        Some((old_index, new_index))
      DiffOp::Insert(old_index~, new_index~, new_len=_) =>
        Some((old_index, new_index))
      DiffOp::Replace(old_index~, old_len=_, new_index~, new_len=_) =>
        Some((old_index, new_index))
    }
  }

  // Apply stable sorting with defined ordering rules
  self
  .inner()
  .sort_by(fn(a : DiffOp, b : DiffOp) -> Int {
    let (a_old, a_new) = get_old_new_index(a).unwrap()
    let (b_old, b_new) = get_old_new_index(b).unwrap()
    if a_old == b_old {
      // When old positions match, apply secondary sorting rules
      // Delete operations should come before other operations for consistency
      match (a, b) {
        (Delete(..), Delete(..)) => a_new - b_new
        (Delete(..), _) => -1
        (_, Delete(..)) => 1
        _ => a_new - b_new
      }
    } else {
      // Primary sort by old sequence position
      a_old - b_old
    }
  })
}