// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_CONTROLS_BUTTON_SINGLE_ANIMATED_IMAGE_CONTAINER_H_
#define UI_VIEWS_CONTROLS_BUTTON_SINGLE_ANIMATED_IMAGE_CONTAINER_H_

#include <memory>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/lottie/animation.h"
#include "ui/views/controls/button/label_button_image_container.h"
#include "ui/views/views_export.h"

namespace views {

// A LabelButtonImageContainer that displays a single Lottie animation and
// manages its playback state.
class VIEWS_EXPORT SingleAnimatedImageContainer : public SingleImageContainer,
                                                  gfx::AnimationDelegate {
 public:
  // Specifies the direction of the animation.
  enum class AnimationDirection {
    // Play animation in forward direction 0.0 -> 1.0
    kForward,
    // Play animation in reverse direction 1.0 -> 0.0
    kBackward,
  };

  // Specifies the end behavior of the animation.
  enum class AnimationEndBehavior {
    // Pauses the state at the end of animation and shows
    // images corresponding to the last frame.
    kPause,
    // Resets the state of the animation and reverts back
    // to showing the image corresponding to the button state.
    kReset,
  };

  // Defines the animation that should be played.
  struct AnimationDefinition {
    int resource_id;
    SkColor color;
    AnimationDirection direction;
    AnimationEndBehavior end_behavior;
  };

  struct AnimationBoundary {
    float start_offset;
    float end_offset;
  };

  // Defines the configuration of the animation to play.
  struct AnimationConfig {
    std::optional<AnimationBoundary> boundary;
    gfx::Tween::Type tween = gfx::Tween::LINEAR;
    base::TimeDelta duration = base::Milliseconds(0);
  };

  explicit SingleAnimatedImageContainer(LabelButton* button);
  SingleAnimatedImageContainer(const SingleAnimatedImageContainer&) = delete;
  SingleAnimatedImageContainer& operator=(const SingleAnimatedImageContainer&) =
      delete;
  ~SingleAnimatedImageContainer() override;

  bool IsShowingAnimation() const;
  bool HasAnimatedImage(int resource_id) const;
  std::optional<float> animation_progress() const;

  // Play the animation based on the provided definition and the configuration.
  void PlayAnimation(AnimationDefinition definition, AnimationConfig config);
  void PlayAnimation(AnimationDefinition definition,
                     const std::vector<AnimationConfig>& config_cycles);

  // Stops the animation and resets it back to using static images.
  void ResetAnimation();

  void ClearAnimatedImages();

  // LabelButtonImageContainer:
  void UpdateImage(const LabelButton* button) override;

 protected:
  // gfx::AnimationDelegate:
  void AnimationProgressed(const gfx::Animation* animation) override;
  void AnimationEnded(const gfx::Animation* animation) override;

  void AddAnimatedImage(int resource_id);
  virtual std::unique_ptr<lottie::Animation> LoadAnimatedImage(int resource_id);

  struct AnimationState {
    AnimationDefinition definition;
    std::vector<AnimationConfig> config;
    float start_offset = 0.0f;
    float end_offset = 1.0f;
    size_t cycle_index = 0;
  };

  raw_ptr<LabelButton> button_;
  gfx::SlideAnimation slide_animation_;
  std::optional<AnimationState> playing_animation_;
  base::flat_map<int, std::unique_ptr<lottie::Animation>> animated_images_;

 private:
  void ValidateSequence(
      const AnimationDefinition& definition,
      const std::vector<AnimationConfig>& config_cycles) const;
  void PlayNextAnimationCycle();
};

}  // namespace views

#endif  // UI_VIEWS_CONTROLS_BUTTON_SINGLE_ANIMATED_IMAGE_CONTAINER_H_
