// Lightweight stroke prediction — extrapolates the next point from recent
// velocity so the live stroke tip leads the digitizer slightly (lower perceived
// latency). Not a full ink-stroke-modeler; intentionally small and testable.

import 'dart:ui';

class PredictedPoint {
  const PredictedPoint(this.offset, this.pressure);
  final Offset offset;
  final double pressure;
}

class StrokePredictor {
  StrokePredictor({this.lookaheadMs = 12});

  /// How far ahead to project, in milliseconds of recent velocity.
  final double lookaheadMs;

  Offset? _prev;
  double? _prevPressure;
  DateTime? _prevAt;
  Offset _velocity = Offset.zero;

  void reset() {
    _prev = null;
    _prevPressure = null;
    _prevAt = null;
    _velocity = Offset.zero;
  }

  /// Feed a real sample; returns an optional predicted tip ahead of [point].
  PredictedPoint? observe(Offset point, double pressure, {DateTime? at}) {
    final now = at ?? DateTime.now();
    if (_prev != null && _prevAt != null) {
      final dtMs = now.difference(_prevAt!).inMicroseconds / 1000.0;
      if (dtMs > 0.5 && dtMs < 80) {
        final raw = (point - _prev!) * (1000.0 / dtMs);
        // EMA blend to avoid jerky predictions.
        _velocity = Offset(
          _velocity.dx * 0.55 + raw.dx * 0.45,
          _velocity.dy * 0.55 + raw.dy * 0.45,
        );
      }
    }
    _prev = point;
    _prevPressure = pressure;
    _prevAt = now;

    if (_velocity.distance < 40) return null; // idle / slow — no predict
    final tip = point + _velocity * (lookaheadMs / 1000.0);
    return PredictedPoint(tip, pressure);
  }

  /// Last known pressure (for predicted tip).
  double get lastPressure => _prevPressure ?? 0.5;
}
