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

#include "chrome/browser/glic/widget/glic_window_animator.h"

#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "chrome/browser/glic/widget/glic_widget.h"
#include "chrome/browser/glic/widget/glic_window_resize_animation.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/views/animation/animation_builder.h"
#include "ui/views/background.h"
#include "ui/views/controls/webview/webview.h"

namespace glic {

GlicWindowAnimator::GlicWindowAnimator(
    base::WeakPtr<GlicWidget> widget,
    base::RepeatingClosure resize_finished_callback)
    : widget_(widget),
      resize_finished_callback_(std::move(resize_finished_callback)) {}

GlicWindowAnimator::~GlicWindowAnimator() = default;

void GlicWindowAnimator::AnimateBounds(const gfx::Rect& target_bounds,
                                       base::TimeDelta duration,
                                       base::OnceClosure callback) {
  if (!widget_) {
    return;
  }

  if (duration < base::Milliseconds(0)) {
    duration = base::Milliseconds(0);
  }

  if (duration > base::Seconds(60)) {
    duration = base::Seconds(60);
  }

  gfx::Rect widget_target_bounds =
      widget_->VisibleToWidgetBounds(target_bounds);

  if (window_resize_animation_) {
    // Update the ongoing animation with the new bounds and new duration.
    window_resize_animation_->UpdateTargetBounds(widget_target_bounds,
                                                 std::move(callback));
    window_resize_animation_->SetDuration(
        std::max(window_resize_animation_->duration_left(), duration));
  } else {
    window_resize_animation_ = std::make_unique<GlicWindowResizeAnimation>(
        widget_, this, widget_target_bounds, duration, std::move(callback));
  }
}

void GlicWindowAnimator::AnimateSize(const gfx::Size& target_size,
                                     base::TimeDelta duration,
                                     base::OnceClosure callback) {
  if (!widget_) {
    return;
  }
  last_target_size_ = target_size;
  gfx::Rect target_bounds = GetCurrentTargetBounds();
  target_bounds.set_size(target_size);
  AnimateBounds(target_bounds, duration, std::move(callback));
}

void GlicWindowAnimator::AnimatePosition(const gfx::Point& target_position,
                                         base::TimeDelta duration,
                                         base::OnceClosure callback) {
  if (!widget_) {
    return;
  }
  // Maintain the size whether there's an ongoing animation or not.
  gfx::Rect new_bounds = GetCurrentTargetBounds();
  new_bounds.set_origin(target_position);
  AnimateBounds(new_bounds, duration, std::move(callback));
}

gfx::Rect GlicWindowAnimator::GetCurrentTargetBounds() {
  if (!widget_) {
    return gfx::Rect();
  }
  if (window_resize_animation_) {
    // Get the ongoing animation's target bounds if they exist.
    return widget_->WidgetToVisibleBounds(
        window_resize_animation_->target_bounds());
  } else {
    return widget_->WidgetToVisibleBounds(widget_->GetWindowBoundsInScreen());
  }
}

bool GlicWindowAnimator::IsAnimating() const {
  return window_resize_animation_ != nullptr;
}

void GlicWindowAnimator::ResetLastTargetSize() {
  last_target_size_ = gfx::Size();
}

void GlicWindowAnimator::MaybeAnimateToTargetSize() {
  if (!widget_) {
    return;
  }
  if (!last_target_size_.IsEmpty() &&
      last_target_size_ != widget_->GetWindowBoundsInScreen().size()) {
    AnimateSize(last_target_size_, base::Milliseconds(300), base::DoNothing());
  }
  ResetLastTargetSize();
}

void GlicWindowAnimator::CancelAnimation() {
  window_resize_animation_.reset();
}

void GlicWindowAnimator::ResizeFinished() {
  if (!widget_) {
    return;
  }
  // Destroy window_resize_animation_.
  window_resize_animation_.reset();
  resize_finished_callback_.Run();
}

}  // namespace glic
