// 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 "chrome/browser/ui/views/omnibox/omnibox_popup_closer.h"

#include "base/logging.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_controller.h"
#include "chrome/browser/ui/omnibox/omnibox_popup_state_manager.h"
#include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"

namespace omnibox {

namespace {

std::string CloseReasonToString(omnibox::PopupCloseReason reason) {
  switch (reason) {
    case omnibox::PopupCloseReason::kBlur:
      return "kBlur";
    case omnibox::PopupCloseReason::kBrowserWidgetMoved:
      return "kBrowserWidgetMoved";
    case omnibox::PopupCloseReason::kEscapeKeyPressed:
      return "kEscapeKeyPressed";
    case omnibox::PopupCloseReason::kLocationIconDragged:
      return "kLocationIconDragged";
    case omnibox::PopupCloseReason::kMouseClickOutside:
      return "kMouseClickOutside";
    case omnibox::PopupCloseReason::kRevertAll:
      return "kRevertAll";
    case omnibox::PopupCloseReason::kTextDrag:
      return "kTextDrag";
    case omnibox::PopupCloseReason::kCrash:
      return "kCrash";
    case omnibox::PopupCloseReason::kOther:
      return "kOther";
  }
}

}  // namespace

DEFINE_USER_DATA(OmniboxPopupCloser);

// static
OmniboxPopupCloser* OmniboxPopupCloser::From(BrowserWindowInterface* browser) {
  return Get(browser->GetUnownedUserDataHost());
}

OmniboxPopupCloser::OmniboxPopupCloser(BrowserView* browser_view,
                                       ui::UnownedUserDataHost& host)
    : browser_view_(browser_view), scoped_unowned_user_data_(host, *this) {
  // Observe UI events on `BrowserView`.
  browser_view_observation_.Observe(browser_view_);
}

OmniboxPopupCloser::~OmniboxPopupCloser() = default;

void OmniboxPopupCloser::OnMouseEvent(ui::MouseEvent* event) {
  // Close the omnibox popup if the click is outside the omnibox view.
  if (!browser_view_->browser()->IsDeleteScheduled()) {
    LocationBar* location_bar = browser_view_->GetLocationBar();
    CHECK(location_bar);
    if (location_bar->ShouldCloseOmniboxPopup(event)) {
      CloseWithReason(PopupCloseReason::kMouseClickOutside);
    }
  }
}

void OmniboxPopupCloser::CloseWithReason(PopupCloseReason reason) {
  VLOG(1) << "Closing omnibox popup with reason: "
          << CloseReasonToString(reason);
  auto* location_bar = browser_view_->GetLocationBar();
  if (auto* popup_view = location_bar->GetOmniboxPopupView()) {
    if (auto* presenter = popup_view->presenter()) {
      // Note: ESC key notifications (`PopupCloseReason::kEscapeKeyPressed`)
      // are handled via `OmniboxPopupWebUIBaseContent::OnPreHandleEscapeKey()`.
      if (presenter->has_active_blockers() ||
          presenter->IsPermissionPromptPreventingClose()) {
        return;
      }
    }
  }
  // Clearing the autocomplete results closes the popup.
  location_bar->GetOmniboxController()->StopAutocomplete(
      /*clear_result=*/true);
  // Reset focus ring for the AIM button if it was set.
  if (auto* omnibox_view = location_bar->GetOmniboxView()) {
    omnibox_view->ApplyFocusRingToAimButton(false);
  }
  // For `kRevertAll` ensure the popup state is reset back to `kNone`.
  if (reason == PopupCloseReason::kRevertAll) {
    if (auto* state_manager =
            location_bar->GetOmniboxController()->popup_state_manager()) {
      state_manager->SetPopupState(OmniboxPopupState::kNone);
    }
  }
}

}  // namespace omnibox
