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

#include "ui/views/windows_stationarity_monitor_aura.h"

#include <algorithm>
#include <vector>

#include "base/no_destructor.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/buildflags.h"

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif  // BUILDFLAG(ENABLE_DESKTOP_AURA)

namespace views {

WindowsStationarityMonitorAura::WindowsStationarityMonitorAura() {
  aura::Env::GetInstance()->AddObserver(this);
  for (aura::WindowTreeHost* window_tree_host :
       aura::Env::GetInstance()->window_tree_hosts()) {
    OnHostInitialized(window_tree_host);
  }
}

WindowsStationarityMonitorAura::~WindowsStationarityMonitorAura() {
  for (aura::Window* window : tracked_windows_) {
    window->RemoveObserver(this);
  }
  aura::Env::GetInstance()->RemoveObserver(this);
  tracked_windows_.clear();
}

// static
WindowsStationarityMonitorAura* WindowsStationarityMonitorAura::GetInstance() {
  static base::NoDestructor<WindowsStationarityMonitorAura> instance;
  return instance.get();
}

void WindowsStationarityMonitorAura::OnHostInitialized(
    aura::WindowTreeHost* host) {
  auto* window = host->window();
  CHECK(window);

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
  auto* native_widget = DesktopNativeWidgetAura::ForWindow(window);
  // Non client widget, such as tooltip widget, might be destroyed right before
  // handling mouse events on other windows, which results in those events being
  // ignored.
  if (!native_widget ||
      !Widget::RequiresNonClientView(native_widget->widget_type())) {
    return;
  }
#endif

  tracked_windows_.push_back(window);
  window->AddObserver(this);
}

void WindowsStationarityMonitorAura::OnWindowDestroying(aura::Window* window) {
  std::erase(tracked_windows_, window);
  NotifyWindowStationaryStateChanged();
}

void WindowsStationarityMonitorAura::OnWindowBoundsChanged(
    aura::Window* window,
    const gfx::Rect& old_bounds,
    const gfx::Rect& new_bounds,
    ui::PropertyChangeReason reason) {
  // We will consider from-animation reason the same as not-from-animation
  // reason. The main consumer |InputEventActivationProtector| will block input
  // event even when the window bounds change due to animation.
  NotifyWindowStationaryStateChanged();
}

// static
WindowsStationarityMonitor* WindowsStationarityMonitor::GetInstance() {
  return WindowsStationarityMonitorAura::GetInstance();
}

}  // namespace views
