import 'dart:math' as math;

import 'package:flutter/material.dart';


class AppLoadingRing extends StatefulWidget {
  const AppLoadingRing({super.key, this.size = 44, this.color});


  final double size;


  final Color? color;

  @override
  State<AppLoadingRing> createState() => _AppLoadingRingState();
}

class _AppLoadingRingState extends State<AppLoadingRing>
    with SingleTickerProviderStateMixin {
  late final AnimationController _rotationController;

  @override
  void initState() {
    super.initState();
    _rotationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1100),
    )..repeat();
  }

  @override
  void dispose() {
    _rotationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final ringColor =
        widget.color ??
        IconTheme.of(context).color ??
        Theme.of(context).colorScheme.onSurface;
    return TweenAnimationBuilder<double>(
      tween: Tween<double>(begin: 0, end: 1),
      duration: const Duration(milliseconds: 200),
      curve: Curves.easeOut,
      builder: (context, fadeInOpacity, child) =>
          Opacity(opacity: fadeInOpacity, child: child),
      child: RepaintBoundary(
        child: SizedBox(
          width: widget.size,
          height: widget.size,
          child: AnimatedBuilder(
            animation: _rotationController,
            builder: (context, _) => CustomPaint(
              painter: _LoadingRingPainter(
                rotationTurns: _rotationController.value,
                color: ringColor,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _LoadingRingPainter extends CustomPainter {
  _LoadingRingPainter({required this.rotationTurns, required this.color});


  final double rotationTurns;


  final Color color;

  static const double strokeWidth = 3;


  static const double _sweepAngle = math.pi * 5 / 3;

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (math.min(size.width, size.height) - strokeWidth) / 2;
    final arcRect = Rect.fromCircle(center: center, radius: radius);
    final rotationAngle = rotationTurns * 2 * math.pi;

    final gradient = SweepGradient(
      startAngle: 0,
      endAngle: 2 * math.pi,
      transform: GradientRotation(rotationAngle),
      colors: [
        color.withValues(alpha: 0.0),
        color.withValues(alpha: 0.25),
        color,
      ],
      stops: const [0.0, 0.5, _sweepAngle / (2 * math.pi)],
    );

    final arcPaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round
      ..shader = gradient.createShader(arcRect);

    canvas.drawArc(arcRect, rotationAngle, _sweepAngle, false, arcPaint);
  }

  @override
  bool shouldRepaint(_LoadingRingPainter oldDelegate) =>
      oldDelegate.rotationTurns != rotationTurns || oldDelegate.color != color;
}
