// Pure word-search helpers — no DOM, so they're unit-testable. Used by
// WordSearchGame.svelte for drag selection and find-matching.

/**
 * Snap a drag from `start` to `end` onto the nearest of the 8 straight
 * directions and return the colinear cells, clamped to the grid. Guarantees a
 * straight path — a non-straight drag is snapped, never returned bent.
 * @param {[number,number]} start
 * @param {[number,number]} end
 * @param {number} size
 * @returns {[number,number][]}
 */
export function lineFrom(start, end, size) {
  const [r0, c0] = start, [r1, c1] = end;
  let dr = r1 - r0, dc = c1 - c0;
  if (dr === 0 && dc === 0) return [start];
  const adr = Math.abs(dr), adc = Math.abs(dc);
  if (adr > adc * 2) dc = 0;            // mostly vertical → snap orthogonal
  else if (adc > adr * 2) dr = 0;       // mostly horizontal → snap orthogonal
  else { const m = Math.max(adr, adc); dr = Math.sign(dr) * m; dc = Math.sign(dc) * m; } // diagonal
  const steps = Math.max(Math.abs(dr), Math.abs(dc));
  const sr = Math.sign(dr), sc = Math.sign(dc), cells = [];
  for (let i = 0; i <= steps; i++) {
    const r = r0 + sr * i, c = c0 + sc * i;
    if (r < 0 || r >= size || c < 0 || c >= size) break;
    cells.push([r, c]);
  }
  return cells.length ? cells : [start];
}

/**
 * Map a pointer position to a grid cell. Pure geometry (the DOM rect is passed
 * in) so it's testable: `rect` is {left, top, width}, cells share the inner width
 * (grid width minus padding/border) evenly, and the result is clamped to range.
 * @returns {[number,number]} [row, col]
 */
export function cellFromPoint(rect, clientX, clientY, n, pad = 7) {
  const cw = (rect.width - 2 * pad) / n;
  const c = Math.min(n - 1, Math.max(0, Math.floor((clientX - rect.left - pad) / cw)));
  const r = Math.min(n - 1, Math.max(0, Math.floor((clientY - rect.top - pad) / cw)));
  return [r, c];
}

/**
 * Read the letters along `cells` off `grid` (array of row strings) and return
 * the matching not-yet-found word (forward or reversed), or null. Re-selecting
 * an already-found word, or a non-word, returns null — harmless.
 * @returns {string|null}
 */
export function matchWord(cells, grid, words, found) {
  if (!cells || cells.length < 2) return null;
  const word = cells.map(([r, c]) => grid[r][c]).join('');
  const rev = [...word].reverse().join('');
  if (words.includes(word) && !found.includes(word)) return word;
  if (words.includes(rev) && !found.includes(rev)) return rev;
  return null;
}
