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

class RepeatOnHoldButton extends StatefulWidget {
  /// Function to be called on tap and on long press.
  /// Return [false] to signify that the loop should be broken after execution.
  final bool? Function() onTap;

  /// Specifies whether repetition speeds up when the user keeps holding the button.
  final bool increaseSpeed;

  /// Specifies how many ms should pass before action is repeated.
  final int initialRepetitionIntervalMs;

  /// Specifies by how much the interval between actions should be divided after [speedUpAfterTimes] times.
  final int speedUpFactor;

  /// Specifies how many times [onTap] will be called before increasing the speed.
  final int speedUpAfterTimes;

  final Widget child;

  const RepeatOnHoldButton({
    Key? key,
    required this.onTap,
    this.increaseSpeed = true,
    this.initialRepetitionIntervalMs = 250,
    this.speedUpFactor = 2,
    this.speedUpAfterTimes = 5,
    required this.child,
  }) : super(key: key);

  @override
  _RepeatOnHoldButtonState createState() => _RepeatOnHoldButtonState();
}

class _RepeatOnHoldButtonState extends State<RepeatOnHoldButton> {
  bool _isHeld = false;

  void onLongPress() async {
    setState(() {
      _isHeld = true;
    });
    int holdCycle = 0;
    int speed = widget.initialRepetitionIntervalMs;

    while (true) {
      final result = widget.onTap() ?? true;
      if (!_isHeld || !result) {
        break;
      }

      holdCycle++;
      if (speed > 1 && holdCycle % widget.speedUpAfterTimes == 0) {
        speed = max(1, (speed ~/ widget.speedUpFactor));
      }

      await Future.delayed(
        Duration(
          milliseconds: speed,
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPress: onLongPress,
      onLongPressEnd: (_) => _isHeld = false,
      child: widget.child,
    );
  }
}
