// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'action_buttons.dart';
import 'theme.dart';

// Examples can assume:
// late BuildContext context;

/// A [ActionIconThemeData] that overrides the default icons of
/// [BackButton], [CloseButton], [DrawerButton], and [EndDrawerButton] with
/// [ActionIconTheme.of] or the overall [Theme]'s [ThemeData.actionIconTheme].
@immutable
class ActionIconThemeData with Diagnosticable {
  /// Creates an [ActionIconThemeData].
  ///
  /// The builders [backButtonIconBuilder], [closeButtonIconBuilder],
  /// [drawerButtonIconBuilder], [endDrawerButtonIconBuilder] may be null.
  const ActionIconThemeData({
    this.backButtonIconBuilder,
    this.closeButtonIconBuilder,
    this.drawerButtonIconBuilder,
    this.endDrawerButtonIconBuilder,
  });

  /// Overrides [BackButtonIcon]'s icon.
  ///
  /// If [backButtonIconBuilder] is null, then [BackButtonIcon]
  /// fallbacks to the platform's default back button icon.
  final WidgetBuilder? backButtonIconBuilder;

  /// Overrides [CloseButtonIcon]'s icon.
  ///
  /// If [closeButtonIconBuilder] is null, then [CloseButtonIcon]
  /// fallbacks to the platform's default close button icon.
  final WidgetBuilder? closeButtonIconBuilder;

  /// Overrides [DrawerButtonIcon]'s icon.
  ///
  /// If [drawerButtonIconBuilder] is null, then [DrawerButtonIcon]
  /// fallbacks to the platform's default drawer button icon.
  final WidgetBuilder? drawerButtonIconBuilder;

  /// Overrides [EndDrawerButtonIcon]'s icon.
  ///
  /// If [endDrawerButtonIconBuilder] is null, then [EndDrawerButtonIcon]
  /// fallbacks to the platform's default end drawer button icon.
  final WidgetBuilder? endDrawerButtonIconBuilder;

  /// Creates a copy of this object but with the given fields replaced with the
  /// new values.
  ActionIconThemeData copyWith({
    WidgetBuilder? backButtonIconBuilder,
    WidgetBuilder? closeButtonIconBuilder,
    WidgetBuilder? drawerButtonIconBuilder,
    WidgetBuilder? endDrawerButtonIconBuilder,
  }) {
    return ActionIconThemeData(
      backButtonIconBuilder: backButtonIconBuilder ?? this.backButtonIconBuilder,
      closeButtonIconBuilder: closeButtonIconBuilder ?? this.closeButtonIconBuilder,
      drawerButtonIconBuilder: drawerButtonIconBuilder ?? this.drawerButtonIconBuilder,
      endDrawerButtonIconBuilder: endDrawerButtonIconBuilder ?? this.endDrawerButtonIconBuilder,
    );
  }

  /// Linearly interpolate between two action icon themes.
  static ActionIconThemeData? lerp(ActionIconThemeData? a, ActionIconThemeData? b, double t) {
    if (a == null && b == null) {
      return null;
    }
    return ActionIconThemeData(
      backButtonIconBuilder: t < 0.5 ? a?.backButtonIconBuilder : b?.backButtonIconBuilder,
      closeButtonIconBuilder: t < 0.5 ? a?.closeButtonIconBuilder : b?.closeButtonIconBuilder,
      drawerButtonIconBuilder: t < 0.5 ? a?.drawerButtonIconBuilder : b?.drawerButtonIconBuilder,
      endDrawerButtonIconBuilder: t < 0.5
          ? a?.endDrawerButtonIconBuilder
          : b?.endDrawerButtonIconBuilder,
    );
  }

  @override
  int get hashCode {
    final values = <Object?>[
      backButtonIconBuilder,
      closeButtonIconBuilder,
      drawerButtonIconBuilder,
      endDrawerButtonIconBuilder,
    ];
    return Object.hashAll(values);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is ActionIconThemeData &&
        other.backButtonIconBuilder == backButtonIconBuilder &&
        other.closeButtonIconBuilder == closeButtonIconBuilder &&
        other.drawerButtonIconBuilder == drawerButtonIconBuilder &&
        other.endDrawerButtonIconBuilder == endDrawerButtonIconBuilder;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(
      DiagnosticsProperty<WidgetBuilder>(
        'backButtonIconBuilder',
        backButtonIconBuilder,
        defaultValue: null,
      ),
    );
    properties.add(
      DiagnosticsProperty<WidgetBuilder>(
        'closeButtonIconBuilder',
        closeButtonIconBuilder,
        defaultValue: null,
      ),
    );
    properties.add(
      DiagnosticsProperty<WidgetBuilder>(
        'drawerButtonIconBuilder',
        drawerButtonIconBuilder,
        defaultValue: null,
      ),
    );
    properties.add(
      DiagnosticsProperty<WidgetBuilder>(
        'endDrawerButtonIconBuilder',
        endDrawerButtonIconBuilder,
        defaultValue: null,
      ),
    );
  }
}

/// An inherited widget that overrides the default icon of [BackButtonIcon],
/// [CloseButtonIcon], [DrawerButtonIcon], and [EndDrawerButtonIcon] in this
/// widget's subtree.
///
/// {@tool dartpad}
/// This example shows how to define custom builders for drawer and back
/// buttons.
///
/// ** See code in examples/api/lib/material/action_buttons/action_icon_theme.0.dart **
/// {@end-tool}
class ActionIconTheme extends InheritedTheme {
  /// Creates a theme that overrides the default icon of [BackButtonIcon],
  /// [CloseButtonIcon], [DrawerButtonIcon], and [EndDrawerButtonIcon] in this
  /// widget's subtree.
  const ActionIconTheme({super.key, required this.data, required super.child});

  /// Specifies the default icon overrides for descendant [BackButtonIcon],
  /// [CloseButtonIcon], [DrawerButtonIcon], and [EndDrawerButtonIcon] widgets.
  final ActionIconThemeData data;

  /// Retrieves the [ActionIconThemeData] from the closest ancestor [ActionIconTheme]
  /// widget.
  ///
  /// If there is no enclosing [ActionIconTheme] widget, then
  /// [ThemeData.actionIconTheme] is used.
  ///
  /// Typical usage is as follows:
  ///
  /// ```dart
  /// ActionIconThemeData? theme = ActionIconTheme.of(context);
  /// ```
  static ActionIconThemeData? of(BuildContext context) {
    final ActionIconTheme? actionIconTheme = context
        .dependOnInheritedWidgetOfExactType<ActionIconTheme>();
    return actionIconTheme?.data ?? Theme.of(context).actionIconTheme;
  }

  @override
  Widget wrap(BuildContext context, Widget child) {
    return ActionIconTheme(data: data, child: child);
  }

  @override
  bool updateShouldNotify(ActionIconTheme oldWidget) => data != oldWidget.data;
}
