// Copyright 2012 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/location_bar/selected_keyword_view.h"

#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
#include "chrome/browser/history_embeddings/history_embeddings_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/omnibox/omnibox_controller.h"
#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
#include "components/history_embeddings/core/history_embeddings_features.h"
#include "components/omnibox/browser/vector_icons.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/template_url_starter_pack_data.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/theme_provider.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/property_effects.h"

namespace {

constexpr auto kForegroundColorId = kColorOmniboxKeywordSelected;

}  // namespace

// static
SelectedKeywordView::KeywordLabelNames
SelectedKeywordView::GetKeywordLabelNames(const std::u16string& keyword,
                                          const TemplateURLService* service) {
  return searchbox::GetKeywordLabelNames(keyword, service);
}

// static
ui::ImageModel SelectedKeywordView::GetKeywordIcon(
    const std::u16string& keyword,
    const OmniboxController* omnibox_controller,
    Profile* profile) {
  const int icon_size = GetLayoutConstant(LayoutConstant::kLocationBarIconSize);

  const TemplateURLService* service =
      TemplateURLServiceFactory::GetForProfile(profile);
  const TemplateURL* template_url = service->GetTemplateURLForKeyword(keyword);

  // Check for custom icon image first.
  gfx::Image image;
  if (template_url &&
      (template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION)) {
    image = extensions::OmniboxAPI::Get(profile)->GetOmniboxIcon(
        template_url->GetExtensionId());
  } else if (template_url &&
             template_url->policy_origin() ==
                 TemplateURLData::PolicyOrigin::kSearchAggregator) {
    const SkBitmap* bitmap = omnibox_controller->edit_model()->GetIconBitmap(
        template_url->favicon_url());
    if (bitmap) {
      image = gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap));
    }
  }

  if (!image.IsEmpty()) {
    return ui::ImageModel::FromImageSkia(
        gfx::ImageSkiaOperations::CreateResizedImage(
            image.AsImageSkia(),
            skia::ImageOperations::ResizeMethod::RESIZE_LANCZOS3,
            gfx::Size(icon_size, icon_size)));
  }

  // Use the search icon for most keywords.
  auto* vector_icon =
      &(features::IsRoundedIconsEnabled() ? vector_icons::kSearchIcon
                                          : vector_icons::kSearchOldIcon);

  if (template_url &&
      template_url->starter_pack_id() ==
          template_url_starter_pack_data::StarterPackId::kGemini) {
    vector_icon = &omnibox::kSparkIcon;
  } else if (template_url &&
             template_url->starter_pack_id() ==
                 template_url_starter_pack_data::StarterPackId::kAiMode) {
    vector_icon =
        &(features::IsRoundedIconsEnabled() ? omnibox::kSearchSparkIcon
                                            : omnibox::kSearchSparkOldIcon);
  } else if (history_embeddings::IsHistoryEmbeddingsEnabledForProfile(
                 profile) &&
             history_embeddings::GetFeatureParameters().omnibox_scoped &&
             template_url &&
             template_url->starter_pack_id() ==
                 template_url_starter_pack_data::StarterPackId::kHistory) {
    vector_icon =
        &(features::IsRoundedIconsEnabled() ? omnibox::kSearchSparkIcon
                                            : omnibox::kSearchSparkOldIcon);
  } else if (template_url &&
             template_url->policy_origin() ==
                 TemplateURLData::PolicyOrigin::kSearchAggregator) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
    vector_icon = base::FeatureList::IsEnabled(omnibox::kUseAgentspace25Logo)
                      ? &vector_icons::kGoogleAgentspaceMonochromeLogo25Icon
                      : &vector_icons::kGoogleAgentspaceMonochromeLogoIcon;
#endif
  }

  return ui::ImageModel::FromVectorIcon(*vector_icon, kForegroundColorId,
                                        icon_size);
}

SelectedKeywordView::SelectedKeywordView(
    IconLabelBubbleView::Delegate* delegate,
    Profile* profile,
    const OmniboxController* omnibox_controller,
    const gfx::FontList& font_list)
    : IconLabelBubbleView(font_list, delegate),
      profile_(profile),
      omnibox_controller_(omnibox_controller) {
  full_label_.SetFontList(font_list);
  full_label_.SetVisible(false);
  partial_label_.SetFontList(font_list);
  partial_label_.SetVisible(false);
  label()->SetElideBehavior(gfx::FADE_TAIL);

  // TODO(crbug.com/40890218): `IconLabelBubbleView::GetAccessibleNodeData`
  // would set the name to explicitly empty when the name was missing.
  // That function no longer exists. As a result we need to handle that here.
  // Regarding this view's namelessness: Until this view has a keyword and
  // labels with text, there will be no accessible name. But this view claims to
  // be focusable, so paint checks will fail due to a lack of name. It might
  // make more sense to only set `FocusBehavior` when this view will be shown.
  // For now, Eliminate the paint check failure.
  if (GetViewAccessibility().GetCachedName().empty()) {
    GetViewAccessibility().SetName(
        std::u16string(), ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
  }
  GetViewAccessibility().SetLiveRegionContainer(
      views::ViewAccessibility::LiveRegionStatus::kPolite,
      views::ViewAccessibility::kLiveRegionRelevantDefault, /*atomic=*/true);
}

SelectedKeywordView::~SelectedKeywordView() = default;

void SelectedKeywordView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  SetLabelForCurrentWidth();
}

SkColor SelectedKeywordView::GetForegroundColor() const {
  return GetColorProvider()->GetColor(kForegroundColorId);
}

gfx::Size SelectedKeywordView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  // Height will be ignored by the LocationBarView.
  return GetSizeForLabelWidth(full_label_.GetPreferredSize().width());
}

gfx::Size SelectedKeywordView::GetMinimumSize() const {
  // Height will be ignored by the LocationBarView.
  return GetSizeForLabelWidth(0);
}

void SelectedKeywordView::OnThemeChanged() {
  IconLabelBubbleView::OnThemeChanged();
  UpdateIcon();
}

void SelectedKeywordView::SetKeyword(const std::u16string& keyword) {
  if (keyword_ == keyword) {
    return;
  }
  keyword_ = keyword;
  OnPropertyChanged(&keyword_, views::PropertyEffects::kNone);
  UpdateIcon();

  const auto* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile_);
  // TODO(pkasting): Arguably, much of the code below would be better as
  // property change handlers in file-scope subclasses of Label etc.
  if (keyword.empty() || !template_url_service) {
    return;
  }

  KeywordLabelNames names = GetKeywordLabelNames(keyword, template_url_service);
  full_label_.SetText(names.full_name);
  partial_label_.SetText(names.short_name);

  // Update the label now so ShouldShowLabel() works correctly when the parent
  // class is calculating the preferred size. It will be updated again during
  // layout, taking into account how much space has actually been allotted.
  SetLabelForCurrentWidth();
}

const std::u16string& SelectedKeywordView::GetKeyword() const {
  return keyword_;
}

int SelectedKeywordView::GetExtraInternalSpacing() const {
  // Align the label text with the suggestion text.
  return 14;
}

void SelectedKeywordView::SetLabelForCurrentWidth() {
  // Keep showing the full label as long as there's more than enough width for
  // the partial label. Otherwise there will be empty space displayed next to
  // the partial label.
  bool use_full_label =
      width() >
      GetSizeForLabelWidth(partial_label_.GetPreferredSize().width()).width();
  SetLabel(use_full_label ? full_label_.GetText() : partial_label_.GetText());
}

void SelectedKeywordView::UpdateIcon() {
  IconLabelBubbleView::SetImageModel(
      GetKeywordIcon(keyword_, omnibox_controller_, profile_));
}

BEGIN_METADATA(SelectedKeywordView)
ADD_PROPERTY_METADATA(std::u16string, Keyword)
END_METADATA
