import 'dart:math' as math;
import 'dart:ui';

import 'package:image/image.dart' as img;

class RefinedCircle {
  const RefinedCircle({required this.center, required this.radius});
  final Offset center;
  final double radius;
}

/// Refine an AABB into a circular coin contour via radial edge search.
class CoinContourRefiner {
  static RefinedCircle? refine(img.Image image, Rect aabb) {
    if (aabb.width < 16 || aabb.height < 16) return null;

    final cx = aabb.center.dx;
    final cy = aabb.center.dy;
    final maxR = math.max(aabb.width, aabb.height) * 0.62;
    final minR = math.min(aabb.width, aabb.height) * 0.28;
    if (maxR <= minR + 2) {
      return RefinedCircle(
        center: Offset(cx, cy),
        radius: math.min(aabb.width, aabb.height) / 2,
      );
    }

    // Sample edge strength along rays; vote for radius with strongest ring.
    const rays = 64;
    final radiusVotes = <double>[];
    for (var i = 0; i < rays; i++) {
      final ang = (i / rays) * math.pi * 2;
      final dx = math.cos(ang);
      final dy = math.sin(ang);
      var bestR = (minR + maxR) / 2;
      var bestMag = -1.0;
      for (var r = minR; r <= maxR; r += 1.5) {
        final x1 = (cx + dx * r).round();
        final y1 = (cy + dy * r).round();
        final x0 = (cx + dx * (r - 2)).round();
        final y0 = (cy + dy * (r - 2)).round();
        if (!_in(image, x0, y0) || !_in(image, x1, y1)) continue;
        final g0 = _gray(image, x0, y0);
        final g1 = _gray(image, x1, y1);
        final mag = (g1 - g0).abs();
        if (mag > bestMag) {
          bestMag = mag;
          bestR = r;
        }
      }
      if (bestMag > 8) radiusVotes.add(bestR);
    }

    if (radiusVotes.length < rays * 0.35) {
      return RefinedCircle(
        center: Offset(cx, cy),
        radius: math.min(aabb.width, aabb.height) / 2 * 0.96,
      );
    }

    radiusVotes.sort();
    final radius = radiusVotes[radiusVotes.length ~/ 2] * 1.04;

    // Slight center nudge from opposite ray midpoints.
    var nx = 0.0;
    var ny = 0.0;
    var n = 0;
    for (var i = 0; i < rays; i += 2) {
      final ang = (i / rays) * math.pi * 2;
      final r = radiusVotes[math.min(i, radiusVotes.length - 1)];
      nx += cx + math.cos(ang) * (r - radius);
      ny += cy + math.sin(ang) * (r - radius);
      n++;
    }
    final center = n == 0
        ? Offset(cx, cy)
        : Offset(
            ((nx / n) + cx) / 2,
            ((ny / n) + cy) / 2,
          );

    final clamped = Offset(
      center.dx.clamp(radius, image.width - radius),
      center.dy.clamp(radius, image.height - radius),
    );
    return RefinedCircle(center: clamped, radius: radius);
  }

  static bool _in(img.Image image, int x, int y) =>
      x >= 0 && y >= 0 && x < image.width && y < image.height;

  static double _gray(img.Image image, int x, int y) {
    final p = image.getPixel(x, y);
    return 0.299 * p.r + 0.587 * p.g + 0.114 * p.b;
  }
}
