// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

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

/// {@template bloc_base_consumer}
/// [BlocBaseConsumerScreen] exposes [onBuild] and [onListen] in order react
/// to new states.
///
/// An optional [shouldBuildWhen] and [shouldListenWhen] can be implemented
/// for more granular control over when [onListen] and [onBuild] are called.
/// The [shouldListenWhen] and [shouldBuildWhen] will be invoked on
/// each [Bloc] or `state` change.
/// They each take the previous `state` and current `state` and must return
/// a [bool] which determines whether or not the [onBuild] and/or [onListen]
/// function will be invoked.
/// The previous `state` will be initialized to the `state` of the [Bloc] when
/// the [BlocConsumer] is initialized.
/// [shouldListenWhen] and [shouldBuildWhen] are optional and if they
/// aren't implemented, they will default to `true`.
///
/// An optional [parent] can also be implemented. This build a wrapper arround
/// the built [BlocConsumer] that is **not** rebuild on each state.
/// {@endtemplate}
abstract class BlocBaseConsumerScreen<Bloc extends BlocBase<State>,
    State extends Object> extends StatelessWidget {
  /// {@macro bloc_base_consumer}
  const BlocBaseConsumerScreen({super.key});

  /// Takes the previous `state` and the current `state` and is responsible for
  /// returning a [bool] which determines whether or not to trigger
  /// [onBuild] with the current `state`.
  bool shouldBuildWhen(State previous, State current) => true;

  /// Takes the previous `state` and the current `state` and is responsible for
  /// returning a [bool] which determines whether or not to trigger
  /// [onListen] with the current `state`.
  bool shouldListenWhen(State previous, State current) => true;

  /// The [parent] function which will be invoked on build.
  /// The [parent] takes a `BuildContext` that **doesn't have** access
  /// to the [Bloc] or [Cubit].
  Widget parent(BuildContext context, Widget child) => child;

  /// The [onBuild] function which will be invoked on each widget build.
  /// The [onBuild] takes the `BuildContext` and current `state` and
  /// must return a widget.
  Widget onBuild(BuildContext context, State state);

  /// Takes the `BuildContext` along with the `state`
  /// and is responsible for executing in response to `state` changes.
  void onListen(BuildContext context, State state) {}

  @override
  Widget build(BuildContext context) => parent(
        context,
        BlocConsumer<Bloc, State>(
          listenWhen: shouldListenWhen,
          listener: onListen,
          buildWhen: shouldBuildWhen,
          builder: onBuild,
        ),
      );
}
