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

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class SCRoomLiveAudioIndicator extends StatefulWidget {
  const SCRoomLiveAudioIndicator({
    super.key,
    this.width,
    this.height,
    this.color = const Color(0xFF1BF2AE),
    this.duration = const Duration(milliseconds: 1260),
  });

  final double? width;
  final double? height;
  final Color color;
  final Duration duration;

  @override
  State<SCRoomLiveAudioIndicator> createState() =>
      _SCRoomLiveAudioIndicatorState();
}

class _SCRoomLiveAudioIndicatorState extends State<SCRoomLiveAudioIndicator>
    with SingleTickerProviderStateMixin {
  static const List<double> _phaseOffsets = <double>[0.0, 0.18, 0.36];
  static const List<double> _minFactors = <double>[0.28, 0.2, 0.34];
  static const List<double> _maxFactors = <double>[0.9, 0.76, 0.96];

  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration)
      ..repeat();
  }

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

  @override
  Widget build(BuildContext context) {
    final double indicatorWidth = widget.width ?? 14.w;
    final double indicatorHeight = widget.height ?? 14.w;
    return RepaintBoundary(
      child: SizedBox(
        width: indicatorWidth,
        height: indicatorHeight,
        child: AnimatedBuilder(
          animation: _controller,
          builder: (context, child) {
            return CustomPaint(
              painter: _RoomLiveAudioPainter(
                progress: _controller.value,
                color: widget.color,
                phaseOffsets: _phaseOffsets,
                minFactors: _minFactors,
                maxFactors: _maxFactors,
              ),
            );
          },
        ),
      ),
    );
  }
}

class _RoomLiveAudioPainter extends CustomPainter {
  const _RoomLiveAudioPainter({
    required this.progress,
    required this.color,
    required this.phaseOffsets,
    required this.minFactors,
    required this.maxFactors,
  });

  final double progress;
  final Color color;
  final List<double> phaseOffsets;
  final List<double> minFactors;
  final List<double> maxFactors;

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()..style = PaintingStyle.fill;
    final double spacing = size.width * 0.16;
    final double barWidth = (size.width - (spacing * 2)) / 3;
    final double radius = math.min(barWidth / 2, size.height / 2);
    final double baseline = size.height;
    for (int index = 0; index < 3; index++) {
      final double cycleProgress = (progress + phaseOffsets[index]) % 1.0;
      final double wave = _buildWaveValue(cycleProgress);
      final double factor =
          lerpDouble(minFactors[index], maxFactors[index], wave) ??
          minFactors[index];
      final double barHeight = math.max(
        size.height * factor,
        size.height * 0.2,
      );
      final double left = index * (barWidth + spacing);
      final double top = baseline - barHeight;
      paint.color = color.withValues(alpha: 0.68 + (0.32 * wave));
      canvas.drawRRect(
        RRect.fromRectAndRadius(
          Rect.fromLTWH(left, top, barWidth, barHeight),
          Radius.circular(radius),
        ),
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant _RoomLiveAudioPainter oldDelegate) {
    return oldDelegate.progress != progress || oldDelegate.color != color;
  }

  double _buildWaveValue(double t) {
    final double primary = 0.5 - (0.5 * math.cos(2 * math.pi * t));
    final double smoothPrimary = primary * primary * (3 - (2 * primary));
    final double detail = 0.5 - (0.5 * math.cos(4 * math.pi * t));
    final double detailWeight = (1 - ((smoothPrimary - 0.5).abs() * 2)).clamp(
      0.0,
      1.0,
    );
    return (smoothPrimary + ((detail - 0.5) * 0.16 * detailWeight)).clamp(
      0.0,
      1.0,
    );
  }
}
