import 'package:flutter/material.dart';
import 'package:widgets/my_load_image.dart';

class RatingWidget extends StatefulWidget {
  final int count;
  final double maxRating;
  final double value;
  final double size;
  final double padding;
  final String nomalImage;
  final String selectImage;
  final bool selectAble;
  final ValueChanged<String> onRatingUpdate;
  final bool integerOnly;

  RatingWidget({
    required this.nomalImage,
    required this.selectImage,
    required this.onRatingUpdate,
    this.count = 5,
    this.value = 5.0,
    this.maxRating = 5.0,
    this.size = 20,
    this.padding = 0,
    this.selectAble = false,
    this.integerOnly = false,
  });

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

class _RatingWidgetState extends State<RatingWidget> {
  late double value;

  @override
  void initState() {
    super.initState();
    value = widget.value;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: widget.selectAble ? _handleTap : null,
      onHorizontalDragUpdate: widget.selectAble ? _handleDrag : null,
      child: buildRowRating(),
    );
  }

  void _handleTap(TapDownDetails details) {
    _updateValue(details.localPosition.dx);
  }

  void _handleDrag(DragUpdateDetails details) {
    _updateValue(details.localPosition.dx);
  }

  void _updateValue(double dx) {
    if (dx < 0) dx = 0;
    if (dx >= widget.size * widget.count + widget.padding * (widget.count - 1)) {
      value = widget.maxRating;
    } else {
      for (int i = 1; i <= widget.count; i++) {
        if (dx < i * (widget.size + widget.padding)) {
          value = i * (widget.maxRating / widget.count);
          if (dx < (i - 1) * (widget.size + widget.padding) + widget.size) {
            value -= (widget.size - (dx - (i - 1) * (widget.size + widget.padding))) / widget.size * (widget.maxRating / widget.count);
          }
          break;
        }
      }
    }

    if (widget.integerOnly) {
      value = value.ceilToDouble();
    }

    setState(() {
      widget.onRatingUpdate(value.toStringAsFixed(1));
    });
  }

  int fullStars() {
    return (value / (widget.maxRating / widget.count)).floor();
  }

  double star() {
    if (widget.integerOnly) {
      return 0;
    }
    return (value % (widget.maxRating / widget.count)) / (widget.maxRating / widget.count) * widget.size;
  }

  List<Widget> buildRow() {
    int full = fullStars();
    List<Widget> children = [];

    for (int i = 0; i < full; i++) {
      children.add(MyAssetImage(
        widget.selectImage,
        width: widget.size,
        height: widget.size,
      ));

      if (i < widget.count - 1) {
        children.add(SizedBox(width: widget.padding));
      }
    }

    if (full < widget.count) {
      children.add(
        Stack(
          children: [
            MyAssetImage(
              widget.nomalImage,
              width: widget.size,
              height: widget.size,
            ),
            ClipRect(
              clipper: SMClipper(rating: star()),
              child: MyAssetImage(
                widget.selectImage,
                width: widget.size,
                height: widget.size,
              ),
            ),
          ],
        ),
      );
    }

    return children;
  }

  List<Widget> buildNomalRow() {
    List<Widget> children = [];
    for (int i = 0; i < widget.count; i++) {
      children.add(MyAssetImage(
        widget.nomalImage,
        width: widget.size,
        height: widget.size,
      ));
      if (i < widget.count - 1) {
        children.add(SizedBox(width: widget.padding));
      }
    }
    return children;
  }

  Widget buildRowRating() {
    return Stack(
      children: <Widget>[
        Row(children: buildNomalRow()),
        Row(children: buildRow()),
      ],
    );
  }
}

class SMClipper extends CustomClipper<Rect> {
  final double rating;

  SMClipper({required this.rating});

  @override
  Rect getClip(Size size) {
    return Rect.fromLTRB(0.0, 0.0, rating, size.height);
  }

  @override
  bool shouldReclip(SMClipper oldClipper) {
    return rating != oldClipper.rating;
  }
}