import 'package:flutter/material.dart';

enum PredefinedThemes {
  success,
  successOutline,
  danger,
  dangerOutline,
  warning,
  warningOutline,
  info,
  infoOutline,
  primary,
  primaryOutline,
  secondary,
  secondaryOutline,
  dark,
  darkOutline,
  light,
}

List definedColors = [
  // success
  {"color": Color(0xFF5CB85C), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // successOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF5CB85C), "shadowColor": Color(0xFF5CB85C), "blurColor": Color(0xFF5CB85C)},
  // danger
  {"color": Color(0xFFD9534F), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // dangerOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFFD9534F), "shadowColor": Color(0xFFD9534F), "blurColor": Color(0xFFD9534F)},
  // warning
  {"color": Color(0xFFF0AD4E), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // warningOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFFF0AD4E), "shadowColor": Color(0xFFF0AD4E), "blurColor": Color(0xFFF0AD4E)},
  // info
  {"color": Color(0xFF5BC0DE), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // infoOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF5BC0DE), "shadowColor": Color(0xFF5BC0DE), "blurColor": Color(0xFF5BC0DE)},
  // primary
  {"color": Color(0xFF0275D8), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // primaryOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF0275D8), "shadowColor": Color(0xFF0275D8), "blurColor": Color(0xFF0275D8)},
  // secondary
  {"color": Color(0xFF6B5B95), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // secondaryOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF6B5B95), "shadowColor": Color(0xFF6B5B95), "blurColor": Color(0xFF6B5B95)},
  // dark
  {"color": Color(0xFF292B2C), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  // darkOutline
  {"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF292B2C), "shadowColor": Color(0xFF292B2C), "blurColor": Color(0xFF292B2C)},
  {"color": Color(0xFFF7F7F7), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
  {"color": Color(0xFFBFBFC4), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black}
];

class AnimatedButton extends StatefulWidget {
  // * @param onTap  The function that is called whenever the widget is tapped
  final GestureTapCallback onTap;
  final Widget child;
  // * @param type  There are 16 predefined themes from which you can choose from if you dont want to customize the widget yourself
  final PredefinedThemes type;
  // * @param animationCurve  The curve that the animation will follow
  final Curve animationCurve;
  // * @param enabled  To check whether the button is enabbled or not
  final bool enabled;
  // * @param isMultiColor  To check whether the button has multiple color gradient or not
  final bool isMultiColor;
  // * @param isOutline  To check whether the button has a outline or not
  final bool isOutline;
  // * @param darkShadow  To check whether the button has dark or light shadow
  final bool darkShadow;

  // * @param duration  The time that the animation takes in milliseconds
  final int duration;

  // * @param height  The height of the widget
  final double height;
  // * @param width  The width of the widget
  final double width;
  // * @param blurRadius  The radius of the blur effect of the widget
  final double blurRadius;
  // * @param borderRadius  The radius of the borders of the widget
  final double borderRadius;
  // * @param shadowHeightBottom  The height of the shadow and the animation of the widget from the bottom of the child widget
  final double shadowHeightBottom;
  // * @param shadowHeightLeft  The height of the shadow and the animation of the widget from the left of the child widget
  final double shadowHeightLeft;
  // * @param borderWidth  The width of the border of the widget
  final double borderWidth;

  // * @param borderColor  The color of the border of the widget(if type is not null, this will not work, is Outline should be true for this to work)
  final Color borderColor;
  // * @param blurColor  The color of the blur of the widget(if type is not null, this will not work)
  final Color blurColor;
  // * @param color  The color of the widget(if type is not null, this will not work)
  final Color color;
  // * @param shadowColor  The color of the shadow of the widget(if type is not null, this will not work)
  final Color? shadowColor;

  // * @param colors  The list of colors for the gradient for the background of the widget(isMulticolor should be true for this to work)
  final List<Color>? colors;

  AnimatedButton({
    Key? key,
    required this.onTap,
    required this.child,
    this.enabled = true,
    this.type = PredefinedThemes.primary,
    this.color = Colors.blue,
    this.height = 64,
    this.colors,
    this.isMultiColor = false,
    this.darkShadow = true,
    this.width = 200,
    this.duration = 70,
    this.blurRadius = 0,
    this.borderRadius = 12,
    this.animationCurve = Curves.easeIn,
    this.shadowHeightBottom = 4,
    this.shadowHeightLeft = 0,
    this.isOutline = false,
    this.borderColor = Colors.black,
    this.borderWidth = 1,
    this.blurColor = Colors.black,
    this.shadowColor,
  })  : assert(child != null),
        super(key: key);

  @override
  _AnimatedButtonState createState() => _AnimatedButtonState(
        type: this.type,
        color: this.color,
        blurColor: this.blurColor,
        borderColor: this.borderColor,
      );
}

class _AnimatedButtonState extends State<AnimatedButton> {
  late double btnPositionBottom;
  late double btnPositionTop;
  late double btnPositionLeft;
  late double btnPositionRight;
  PredefinedThemes? type;
  Color? color;
  Color? shadowColor;
  Color? borderColor;
  Color? blurColor;
  _AnimatedButtonState({
    this.type,
    this.color,
    this.shadowColor,
    this.borderColor,
    this.blurColor,
  });

  @override
  void initState() {
    super.initState();
    // check if there is a PredefinedThemes type used in the widget
    // if yes, over-ride all the colors of the widget
    if (type != null) {
      int index = type!.index;
      setState(() {
        color = definedColors[index]["color"];
        shadowColor = definedColors[index]["shadowColor"];
        blurColor = definedColors[index]["blurColor"];
        borderColor = definedColors[index]["borderColor"];
      });
    } else {
      setState(() {
        color = widget.color;
        shadowColor = widget.shadowColor;
        blurColor = widget.blurColor;
        borderColor = widget.borderColor;
      });
    }
    // initialize the top widget with the defined bottom and left height
    setState(() {
      btnPositionBottom = widget.shadowHeightBottom;
      btnPositionLeft = widget.shadowHeightLeft;
    });
  }

  @override
  Widget build(BuildContext context) {
    final double height = widget.height - btnPositionBottom;

    return GestureDetector(
      child: Center(
        child: Container(
          width: widget.width + btnPositionLeft,
          height: height + btnPositionBottom,
          child: Stack(
            children: <Widget>[
              Positioned(
                bottom: 0,
                child: Container(
                  height: height,
                  width: widget.width,
                  decoration: BoxDecoration(
                    gradient: widget.isMultiColor
                        ? LinearGradient(
                            colors: shadow(widget.colors!, widget.darkShadow),
                          )
                        : null,
                    color: (widget.shadowColor != null)
                        ? widget.shadowColor
                        : shadow((widget.isOutline) ? [color!] : [borderColor!], widget.darkShadow),
                    borderRadius: BorderRadius.all(
                      Radius.circular(
                        widget.borderRadius,
                      ),
                    ),
                    border: widget.isOutline
                        ? Border.all(
                            color: borderColor!,
                            width: widget.borderWidth,
                          )
                        : null,
                    boxShadow: <BoxShadow>[
                      BoxShadow(
                        color: blurColor!,
                        blurRadius: widget.blurRadius,
                        offset: Offset(0.0, 0.0),
                      ),
                    ],
                  ),
                ),
              ),
              AnimatedPositioned(
                curve: widget.animationCurve,
                duration: Duration(milliseconds: widget.duration),
                bottom: btnPositionBottom,
                left: btnPositionLeft,
                child: Container(
                  height: height,
                  width: widget.width,
                  decoration: BoxDecoration(
                    gradient: widget.isMultiColor
                        ? LinearGradient(
                            colors: widget.colors!,
                          )
                        : null,
                    color: color,
                    borderRadius: BorderRadius.all(
                      Radius.circular(
                        widget.borderRadius,
                      ),
                    ),
                    border: widget.isOutline
                        ? Border.all(
                            color: borderColor!,
                            width: widget.borderWidth,
                          )
                        : null,
                  ),
                  child: Center(
                    child: widget.child,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
      onTapDown: widget.enabled ? _tapDown : null,
      onTapUp: widget.enabled ? _tapUp : null,
      onTapCancel: widget.enabled ? _unTap : null,
    );
  }

  void _tapDown(_) {
    // when the widget is pressed make both the heights, bottom and left to zero
    setState(() {
      btnPositionBottom = 0;
      btnPositionLeft = 0;
    });
  }

  // when the widget is released after being pressed, it returns to the given heights, bottom and left
  void _tapUp(_) => _unTap();

  void _unTap() {
    setState(() {
      btnPositionLeft = widget.shadowHeightLeft;

      btnPositionBottom = widget.shadowHeightBottom;
    });
    widget.onTap();
  }

  // returns a list of color shades according to the params
  // * @param colors list of colors
  // * @param darkshadow whether to return a darker shadow or lighter shadow
  shadow(List<Color> colors, bool darkShadow) {
    List<Color> w = [];

    colors.forEach((color) {
      double amount = darkShadow ? 0.22 : 0.1;
      final hsl = HSLColor.fromColor(color);
      final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
      w.add(hslDark.toColor());
    });
    return (w.length == 1) ? w[0] : w;
  }
}
