/**
 * Text selection: track anchor/focus, overlay selected cells, extract text, copy.
 *
 * Selection operates in screen-space on the FrameBuffer — it doesn't know
 * about nodes or scroll offsets. The overlay is applied as a post-paint
 * pass before diffing, using Attr.Inverse XOR to highlight.
 */
import { Attr } from "./frame-buffer.jsx";
export function createSelection() {
  return {
    anchor: null,
    focus: null
  };
}
export function clearSelection(state) {
  state.anchor = null;
  state.focus = null;
}

/** Normalize anchor/focus into reading-order start/end. */
export function selectionRange(state) {
  if (!state.anchor || !state.focus) return null;
  const a = state.anchor;
  const b = state.focus;

  // Reading order: top-to-bottom, left-to-right
  if (a.row < b.row || a.row === b.row && a.col <= b.col) {
    return {
      startRow: a.row,
      startCol: a.col,
      endRow: b.row,
      endCol: b.col
    };
  }
  return {
    startRow: b.row,
    startCol: b.col,
    endRow: a.row,
    endCol: a.col
  };
}

/** Toggle Attr.Inverse on selected cells in the buffer. */
export function applySelectionOverlay(buf, state) {
  const range = selectionRange(state);
  if (!range) return;
  const {
    startRow,
    startCol,
    endRow,
    endCol
  } = range;
  for (let row = startRow; row <= endRow && row < buf.rows; row++) {
    let colStart = row === startRow ? Math.max(0, startCol) : 0;
    let colEnd = row === endRow ? Math.min(buf.cols - 1, endCol) : buf.cols - 1;

    // Clip leading empty cells on all rows
    while (colStart < colEnd && buf.get(colStart, row).char === " ") {
      colStart++;
    }
    // Clip trailing empty cells on non-end rows (end row follows cursor)
    if (row !== endRow) {
      while (colEnd > colStart && buf.get(colEnd, row).char === " ") {
        colEnd--;
      }
    }
    // Entirely blank row — skip it
    if (colStart === colEnd && buf.get(colStart, row).char === " ") continue;
    for (let col = colStart; col <= colEnd; col++) {
      // getMutable materializes blank slots so highlighted gaps invert too
      // (and never mutates the shared frozen blank).
      const cell = buf.getMutable(col, row);
      // XOR inverse for visual highlight
      cell.attrs ^= Attr.Inverse;
    }
  }
}

/** Extract text from selected cells, joining rows with newlines. */
export function getSelectedText(buf, state) {
  const range = selectionRange(state);
  if (!range) return "";
  const {
    startRow,
    startCol,
    endRow,
    endCol
  } = range;
  const lines = [];
  for (let row = startRow; row <= endRow && row < buf.rows; row++) {
    const colStart = row === startRow ? Math.max(0, startCol) : 0;
    const colEnd = row === endRow ? Math.min(buf.cols - 1, endCol) : buf.cols - 1;
    let line = "";
    for (let col = colStart; col <= colEnd; col++) {
      const cell = buf.get(col, row);
      if (cell.width === 0) continue; // skip continuation cells
      line += cell.char;
    }
    lines.push(line.trimEnd());
  }

  // Trim trailing empty lines
  while (lines.length > 0 && lines[lines.length - 1] === "") {
    lines.pop();
  }
  return lines.join("\n");
}

/** Copy text to system clipboard via OSC 52. */
export function copyToClipboard(text, write) {
  if (!text) return;
  const encoded = Buffer.from(text, "utf-8").toString("base64");
  // OSC 52: set clipboard 'c' (system clipboard), BEL-terminated for compatibility
  write(`\x1b]52;c;${encoded}\x07`);
}
//# sourceMappingURL=selection.jsx.map
