import 'dart:async' show Future;

import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';

/// Displays an Undraw SVG image provided in the applications asset bundle.
///
/// The [assetName] is the path and name of the SVG file in the asset bundle.
/// This SVG asset display widget is tuned for usage with the Undraw images
/// by Katerina Limpitsouni found at https://undraw.co/
/// The [color] property can be used to change the main color theme in the SVG
/// drawing and thus theme the image to fit with colors used in the application.
class SvgAssetImage extends StatefulWidget {
  const SvgAssetImage({
    super.key,
    required this.assetName,
    required this.color,
    this.semanticLabel,
    this.alignment = Alignment.topCenter,
    this.fit = BoxFit.contain,
    this.height,
    this.width,
    this.placeholder,
    this.errorWidget,
    this.padding,
  });

  final String assetName;
  final Color color;
  final String? semanticLabel;
  final Alignment alignment;
  final BoxFit fit;
  final double? height;
  final double? width;
  final Widget? placeholder;
  final Widget? errorWidget;
  final EdgeInsets? padding;

  @override
  State<SvgAssetImage> createState() => _SvgAssetImageState();
}

class _SvgAssetImageState extends State<SvgAssetImage> {
  late Future<SvgPicture> svgImage;
  // The SVG asset as loaded from the asset bundle.
  late String assetSvgString;

  // The SVG asset with its color changed.
  late String svgString;

  Future<SvgPicture> _coloredUndrawImage(bool load, Color color) async {
    // Convert the color to a HEX RGB string without the alpha value.
    final String valueString =
        color.value32bit.toRadixString(16).padLeft(8, '0').substring(2);
    if (load) assetSvgString = await rootBundle.loadString(widget.assetName);
    // Find the default image 'theme' color in the Undraw SVG, and replace
    // the color with another color string value we want to use instead.
    // We store the original bundle asset and change colors on the original
    // if the same asset is re-used but with new color. We could use previous
    // color as the one to replace on next go and save some memory. However,
    // the color we changed to earlier, might be same as some other color in
    // SVG that is not a part of the original source color designed in these
    // SVGs to be swapped out for a new one. If we then do th with random colors
    // long enough, the SVG will eventually become monochromatic and all colors
    // equal to the new color.
    svgString = assetSvgString.replaceAll('#6c63ff', '#$valueString');
    return SvgPicture.string(
      key: ValueKey<String>('SVG:${widget.assetName}-$color'),
      svgString,
      height: widget.height,
      width: widget.width,
      alignment: widget.alignment,
      fit: widget.fit,
      semanticsLabel: widget.semanticLabel,
    );
  }

  @override
  void initState() {
    super.initState();
    svgImage = _coloredUndrawImage(true, widget.color);
  }

  @override
  void didUpdateWidget(covariant SvgAssetImage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.color != oldWidget.color ||
        widget.assetName != oldWidget.assetName) {
      final bool loadSvgAsset = widget.assetName != oldWidget.assetName;
      svgImage = _coloredUndrawImage(loadSvgAsset, widget.color);
    }
  }

  @override
  Widget build(final BuildContext context) {
    return FutureBuilder<SvgPicture>(
      future: svgImage,
      builder:
          (final BuildContext context, final AsyncSnapshot<Widget> snapshot) {
        if (snapshot.hasData) {
          return Padding(
            padding: widget.padding ?? const EdgeInsets.all(16),
            child: snapshot.data,
          );
        } else if (snapshot.hasError) {
          return ConstrainedBox(
            constraints: BoxConstraints.tightFor(
              height: widget.height,
              width: widget.width,
            ),
            child: Center(
              child: widget.errorWidget ??
                  const Text('Could not load asset image!'),
            ),
          );
        } else {
          return Center(
            child: widget.placeholder ?? const CircularProgressIndicator(),
          );
        }
      },
    );
  }
}
