///|
/// The orientation of a selection: whether the active `position` is at the
/// end (`LTR`) or the start (`RTL`) of the selected range. Mirrors Monaco's
/// `SelectionDirection`.
pub(all) enum SelectionDirection {
  LTR
  RTL
} derive(Eq, Debug)

///|
/// A selection in the editor: a range that carries an orientation. Mirrors
/// Monaco's `core/selection.ts` `Selection`. Both endpoints are model
/// positions (one-based `line_number`, UTF-16 `column`); `selection_start` is
/// the fixed anchor and `position` is the moving active end (the cursor).
///
/// This is pure geometry, like `core/selection.ts`. The model/clipboard
/// boundary — normalizing to document offsets and extracting copy text — lives
/// in the controller layer (`viewer/selection.mbt`), not on this type, matching
/// Monaco's split (clipboard belongs to the editor controller, not the value
/// type).
struct Selection {
  selection_start : @base_common.Position
  position : @base_common.Position
} derive(Eq, Debug)

///|
/// Builds a selection from its anchor (`selection_start`) and active
/// (`position`) endpoints without reordering them; the orientation is kept.
pub fn Selection::Selection(
  selection_start : @base_common.Position,
  position : @base_common.Position,
) -> Selection {
  { selection_start, position, }
}

///|
/// Builds a collapsed-or-oriented selection from two model positions, anchor
/// first. Mirrors Monaco's `Selection.fromPositions`.
pub fn Selection::from_positions(
  start : @base_common.Position,
  end : @base_common.Position,
) -> Selection {
  { selection_start: start, position: end, }
}

///|
/// The active end of the selection (the cursor). Mirrors `getPosition`.
pub fn Selection::get_position(self : Selection) -> @base_common.Position {
  self.position
}

///|
/// The fixed anchor of the selection. Mirrors `getSelectionStart`.
pub fn Selection::get_selection_start(
  self : Selection,
) -> @base_common.Position {
  self.selection_start
}

///|
/// The ordered start position (top-left). Mirrors `Range.getStartPosition`.
pub fn Selection::start(self : Selection) -> @base_common.Position {
  if position_le(self.selection_start, self.position) {
    self.selection_start
  } else {
    self.position
  }
}

///|
/// The ordered end position (bottom-right). Mirrors `Range.getEndPosition`.
pub fn Selection::end(self : Selection) -> @base_common.Position {
  if position_le(self.selection_start, self.position) {
    self.position
  } else {
    self.selection_start
  }
}

///|
/// The orientation of the selection. Mirrors `Selection.getDirection`.
pub fn Selection::direction(self : Selection) -> SelectionDirection {
  if position_eq(self.selection_start, self.start()) {
    LTR
  } else {
    RTL
  }
}

///|
/// Whether the selection covers no text (anchor and active coincide).
pub fn Selection::is_empty(self : Selection) -> Bool {
  position_eq(self.selection_start, self.position)
}

///|
/// A copy with the ordered start moved. Mirrors `Selection.setStartPosition`:
/// the orientation is kept.
pub fn Selection::set_start_position(
  self : Selection,
  start_line_number : Int32,
  start_column : Int32,
) -> Selection {
  let new_start : @base_common.Position = Position(
    start_line_number, start_column,
  )
  if self.direction() == LTR {
    Selection(new_start, self.end())
  } else {
    Selection(self.end(), new_start)
  }
}

///|
/// A copy with the ordered end moved. Mirrors `Selection.setEndPosition`:
/// the orientation is kept.
pub fn Selection::set_end_position(
  self : Selection,
  end_line_number : Int32,
  end_column : Int32,
) -> Selection {
  let new_end : @base_common.Position = Position(end_line_number, end_column)
  if self.direction() == LTR {
    Selection(self.start(), new_end)
  } else {
    Selection(new_end, self.start())
  }
}

///|
/// Whether `a` is at or before `b` in reading order (line, then column).
fn position_le(a : @base_common.Position, b : @base_common.Position) -> Bool {
  a.is_before_or_equal(b)
}

///|
fn position_eq(a : @base_common.Position, b : @base_common.Position) -> Bool {
  a.equals(b)
}

///|
pub extend Selection with Debug::{to_repr}

///|
pub extend Selection with Eq::{equal, not_equal}

///|
pub extend SelectionDirection with Debug::{to_repr}

///|
pub extend SelectionDirection with Eq::{equal, not_equal}