// lib/utils/gps_smoothing.dart
import 'dart:math' as math;

import 'package:latlong2/latlong.dart';

class GpsSmoothing {
  /// Semplifica una traccia GPS con Douglas-Peucker.
  ///
  /// [epsilonMeters] è espresso in metri.
  /// Esempio: 5.0 mantiene una buona forma della traccia eliminando jitter minimo.
  static List<LatLng> douglasPeucker(List<LatLng> points, double epsilonMeters) {
    if (points.length < 3 || epsilonMeters <= 0) return List<LatLng>.from(points);

    final first = points.first;
    final last = points.last;

    double maxDistance = 0;
    int index = 0;

    for (int i = 1; i < points.length - 1; i++) {
      final d = _perpendicularDistanceMeters(points[i], first, last);
      if (d > maxDistance) {
        maxDistance = d;
        index = i;
      }
    }

    if (maxDistance > epsilonMeters) {
      final left = douglasPeucker(points.sublist(0, index + 1), epsilonMeters);
      final right = douglasPeucker(points.sublist(index), epsilonMeters);

      return <LatLng>[...left, ...right.skip(1)];
    }

    return <LatLng>[first, last];
  }

  /// Distanza perpendicolare punto-segmento in metri.
  ///
  /// Usa una proiezione locale equirettangolare, sufficiente per segmenti GPS
  /// consecutivi/attività outdoor. Il parametro di proiezione viene limitato
  /// al segmento [start, end], non alla retta infinita.
  static double _perpendicularDistanceMeters(LatLng p, LatLng start, LatLng end) {
    const metersPerDegreeLat = 111320.0;

    final refLatRad = start.latitude * math.pi / 180.0;
    final metersPerDegreeLon = metersPerDegreeLat * math.cos(refLatRad).abs();

    final x = p.longitude * metersPerDegreeLon;
    final y = p.latitude * metersPerDegreeLat;
    final x1 = start.longitude * metersPerDegreeLon;
    final y1 = start.latitude * metersPerDegreeLat;
    final x2 = end.longitude * metersPerDegreeLon;
    final y2 = end.latitude * metersPerDegreeLat;

    final dx = x2 - x1;
    final dy = y2 - y1;

    if (dx == 0 && dy == 0) {
      final px = x - x1;
      final py = y - y1;
      return math.sqrt(px * px + py * py);
    }

    final rawT = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
    final t = rawT.clamp(0.0, 1.0).toDouble();

    final projX = x1 + t * dx;
    final projY = y1 + t * dy;

    final px = x - projX;
    final py = y - projY;

    return math.sqrt(px * px + py * py);
  }
}
