// lib/editor/engine/stroke_simplify.dart
//
// Ramer–Douglas–Peucker stroke point reduction. A fast Surface-Pen stroke can
// land hundreds of nearly-collinear samples; thinning them before persistence
// shrinks the DB row + speeds re-rasterization (R1/R10 perf) with no visible
// change. Endpoints + perceptually-significant vertices are kept; pressure/tilt
// ride along on the retained points.
//
// Pure geometry over EditorStroke (normalized coords); fully unit-tested. The
// commit path can call this before saveHost; the live in-progress stroke is left
// untouched so drawing stays crisp.

import 'stroke_model.dart';

/// Returns [stroke] with its points reduced by RDP at [tolerance] (perpendicular
/// distance in normalized units; larger = more aggressive). Strokes with <= 2
/// points, or a non-positive tolerance, are returned unchanged.
EditorStroke simplifyStroke(EditorStroke stroke, {double tolerance = 0.0008}) {
  final pts = stroke.points;
  if (pts.length <= 2 || tolerance <= 0) return stroke;

  final keep = List<bool>.filled(pts.length, false);
  keep[0] = true;
  keep[pts.length - 1] = true;
  _rdp(pts, 0, pts.length - 1, tolerance * tolerance, keep);

  final reduced = <EditorPoint>[
    for (var i = 0; i < pts.length; i++)
      if (keep[i]) pts[i],
  ];
  if (reduced.length == pts.length) return stroke;
  return stroke.copyWith(points: reduced);
}

// Iterative-friendly recursion over the index range [first, last].
void _rdp(
  List<EditorPoint> pts,
  int first,
  int last,
  double tolSq,
  List<bool> keep,
) {
  if (last <= first + 1) return;
  var maxDistSq = 0.0;
  var index = -1;
  final ax = pts[first].x, ay = pts[first].y;
  final bx = pts[last].x, by = pts[last].y;
  for (var i = first + 1; i < last; i++) {
    final d = _perpDistSq(pts[i].x, pts[i].y, ax, ay, bx, by);
    if (d > maxDistSq) {
      maxDistSq = d;
      index = i;
    }
  }
  if (maxDistSq > tolSq && index != -1) {
    keep[index] = true;
    _rdp(pts, first, index, tolSq, keep);
    _rdp(pts, index, last, tolSq, keep);
  }
}

/// Squared perpendicular distance of (px,py) from the segment (ax,ay)-(bx,by).
/// Degenerate segment (a==b) falls back to squared distance to the point.
double _perpDistSq(
  double px,
  double py,
  double ax,
  double ay,
  double bx,
  double by,
) {
  final dx = bx - ax, dy = by - ay;
  final lenSq = dx * dx + dy * dy;
  if (lenSq == 0) {
    final ex = px - ax, ey = py - ay;
    return ex * ex + ey * ey;
  }
  final cross = (px - ax) * dy - (py - ay) * dx;
  return (cross * cross) / lenSq;
}
