import 'package:flutter/material.dart';

import 'list_tile_reveal.dart';

/// A discrete [Slider] that is nullable and uses [ListTileReveal] for title
/// and additional expanded guide content.
///
/// The Slider can be set to a [value] that is one step below the [min] value.
/// When the Slider is set to this value, it instead of the set value
/// ill display the [valueDefaultLabel].
///
/// For this value, the [onChanged] callback will be called with a null return
/// value.
///
/// The [value] is expected to be in the range of [min] and [max] or null.
///
/// When [value] is null, the Slider will display the
/// [valueDefaultLabel] and use the value step one below the min value as
/// the Slider position and actually used value.
///
/// The [onChanged] callback is required, the is a separate
/// [enabled] flag to enable or disable the Slider.
///
/// This is a Flutter "Universal" Widget that only depends on the SDK and
/// can be dropped into any application.
class SliderListTileReveal extends StatefulWidget {
  const SliderListTileReveal({
    super.key,
    this.value,
    required this.onChanged,
    required this.enabled,
    this.title,
    this.subtitle,
    this.subtitleReveal,
    this.contentPadding,
    this.trailingWidth,
    this.dense,
    this.revealDense,
    this.min = 0.0,
    this.max = 1.0,
    this.divisions = 100,
    this.valueDisplayScale = 1,
    this.valueDecimalPlaces = 2,
    this.valueHeading,
    this.valueDefaultLabel = 'default',
    this.valueUnitLabel = '',
    this.valueDefaultDisabledLabel,
  })  : assert(min < max, 'Min $min must be smaller than max $max'),
        assert(
            (value == null) || (value >= min && value <= max),
            'Value $value is not null or not between '
            'minimum $min and maximum $max');

  /// The currently selected value for this slider.
  ///
  /// The slider's thumb is drawn at a position that corresponds to this value.
  final double? value;

  /// Called during a drag when the user is selecting a new value for the slider
  /// by dragging.
  ///
  /// The slider passes the new value to the callback but does not actually
  /// change state until the parent widget rebuilds the slider with the new
  /// value.
  ///
  /// If null, the slider will be displayed as disabled.
  ///
  /// The callback provided to onChanged should update the state of the parent
  /// [StatefulWidget] using the [State.setState] method, so that the parent
  /// gets rebuilt.
  final ValueChanged<double?> onChanged;

  /// Whether this slider is enabled.
  ///
  /// Set to false to disable the Slider, it will be displayed as disabled
  /// along with the title and value trailing labels.
  final bool enabled;

  /// The main heading content of the Slider tile.
  ///
  /// Typically a [Text] widget.
  final Widget? title;

  /// Additional content displayed below the title.
  ///
  /// Typically a [Text] widget.
  final Widget? subtitle;

  /// Additional content displayed below the subtitle in a reveal animation.
  ///
  /// Typically a [Text] widget.
  final Widget? subtitleReveal;

  /// The [ListTileReveal]'s internal padding.
  ///
  /// Insets a [ListTileReveal]'s contents.
  ///
  /// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used in M2
  /// and `EdgeInsetsDirectional.only(start: 16.0, end: 24.0)` in M3.
  final EdgeInsetsGeometry? contentPadding;

  /// If non-null, requires the trailing child to have exactly this width.
  final double? trailingWidth;

  /// Whether this list tile is part of a vertically dense list.
  ///
  /// If this property is null then its value is based on [ListTileTheme.dense].
  ///
  /// Dense list tiles default to a smaller height.
  final bool? dense;

  /// Whether the used reveal part of the ListTile is dense.
  ///
  /// If not defined, defaults to true.
  final bool? revealDense;

  /// The minimum value the user can select.
  ///
  /// Defaults to 0.0. Must be less than or equal to [max].
  ///
  /// If the [max] is equal to the [min], then the slider is disabled.
  final double min;

  /// The maximum value the user can select.
  ///
  /// Defaults to 1.0. Must be greater than or equal to [min].
  ///
  /// If the [max] is equal to the [min], then the slider is disabled.
  final double max;

  /// The number of discrete divisions.
  ///
  /// Typically used with built in
  /// label to show the current discrete value.
  ///
  /// Only discrete Slider is supported so
  /// [divisions] must be greater than 0.
  final int divisions;

  /// A display scaling value for the displayed value.
  ///
  /// Typically used to show fractions as % numbers on the Slider.
  /// The [value] and callback [onChanged] will still be in the input
  /// scale.
  ///
  /// Defaults to 0, no scaling.
  final int valueDisplayScale;

  /// Number of decimal places to display the value.
  ///
  /// Defaults to 0.
  final int valueDecimalPlaces;

  /// A label used to show as the unit of the value.
  ///
  /// Defaults to an empty string, now unit shown.
  final String valueUnitLabel;

  /// A heading to show above the value display in the trailing part of
  /// the Slider.
  final String? valueHeading;

  /// THe value to display when the value is null, meaning below the min value,
  /// on the Slider, it is shown both in the label and the trailing value.
  final String valueDefaultLabel;

  /// THe value to display when the value is disabled and null, meaning below
  /// the min value, on the Slider, it is shown both in the label and the
  /// trailing value.
  ///
  /// If not defined, defaults to the [valueDefaultLabel].
  final String? valueDefaultDisabledLabel;

  @override
  State<SliderListTileReveal> createState() => _SliderListTileRevealState();
}

class _SliderListTileRevealState extends State<SliderListTileReveal> {
  @override
  // void didUpdateWidget(covariant SliderListTileReveal oldWidget) {
  //   super.didUpdateWidget(oldWidget);
  //   if (oldWidget.enabled != widget.enabled) {
  //     if (!widget.enabled) {
  //       // If the slider is disabled, set the value to null.
  //       widget.onChanged(null);
  //     }
  //   }
  // }

  @override
  Widget build(BuildContext context) {
    // The value of a discrete step.
    final double step = (widget.max - widget.min) /
        (widget.divisions <= 0 ? 1 : widget.divisions);
    // THe effective min value, one step below given min, this value is used
    // to show default label and return null when selected.
    final double effectiveMin = widget.min - step;

    return ListTileReveal(
      enabled: widget.enabled,
      contentPadding: widget.contentPadding,
      dense: widget.dense,
      revealDense: widget.revealDense,
      title: widget.title,
      subtitleReveal: widget.subtitleReveal,
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          if (widget.subtitle != null) widget.subtitle!,
          Slider(
            min: effectiveMin * widget.valueDisplayScale,
            max: widget.max * widget.valueDisplayScale,
            divisions: widget.divisions + 1,
            label: widget.enabled
                ? widget.value == null ||
                        (widget.value ??
                                effectiveMin * widget.valueDisplayScale) <
                            widget.min * widget.valueDisplayScale
                    ? widget.valueDefaultLabel
                    : (widget.value! * widget.valueDisplayScale)
                        .toStringAsFixed(widget.valueDecimalPlaces)
                : widget.valueDefaultDisabledLabel ?? widget.valueDefaultLabel,
            value: widget.value == null || !widget.enabled
                ? effectiveMin * widget.valueDisplayScale
                : widget.value! * widget.valueDisplayScale,
            onChanged: widget.enabled
                ? (double value) {
                    widget.onChanged(
                        value < widget.min * widget.valueDisplayScale
                            ? null
                            : value / widget.valueDisplayScale);
                  }
                : null,
          ),
        ],
      ),
      trailing: SizedBox(
        width: widget.trailingWidth,
        child: Padding(
          padding: const EdgeInsetsDirectional.only(end: 5),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              if (widget.valueHeading != null)
                Text(
                  widget.valueHeading!,
                  style: const TextStyle(fontSize: 11),
                ),
              Text(
                widget.enabled
                    ? widget.value == null ||
                            (widget.value ??
                                    effectiveMin * widget.valueDisplayScale) <
                                widget.min * widget.valueDisplayScale
                        ? 'default\n${widget.valueDefaultLabel}'
                        // ignore: lines_longer_than_80_chars, str interpolation
                        : '${(widget.value! * widget.valueDisplayScale).toStringAsFixed(widget.valueDecimalPlaces)}${widget.valueUnitLabel}'
                    // ignore: lines_longer_than_80_chars, string interpolation
                    : 'default\n${widget.valueDefaultDisabledLabel ?? widget.valueDefaultLabel}',
                textAlign: TextAlign.center,
                style: const TextStyle(
                  fontSize: 11,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
