// Copyright 2018 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/webauthn/hover_list_view.h"

#include <algorithm>
#include <memory>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/views/webauthn/webauthn_hover_button.h"
#include "chrome/browser/ui/webauthn/hover_list_model.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/dialog_client_view.h"
#include "ui/views/window/dialog_delegate.h"

namespace {

std::unique_ptr<WebAuthnHoverButton> CreateHoverButtonForListItem(
    const ui::ImageModel& icon,
    std::u16string item_title,
    std::u16string item_description,
    bool enabled,
    views::Button::PressedCallback callback) {
  constexpr int kChevronSize = 20;
  auto secondary_view =
      std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
          features::IsRoundedIconsEnabled()
              ? vector_icons::kKeyboardArrowRightFlippableIcon
              : vector_icons::kSubmenuArrowChromeRefreshOldIcon,
          enabled ? ui::kColorIcon : ui::kColorIconDisabled, kChevronSize));

  const int kIconSize = 24;
  auto item_image = std::make_unique<views::ImageView>(icon);
  item_image->SetImageSize(gfx::Size(kIconSize, kIconSize));

  return std::make_unique<WebAuthnHoverButton>(
      std::move(callback), std::move(item_image), item_title, item_description,
      std::move(secondary_view), enabled);
}

}  // namespace

// HoverListView ---------------------------------------------------------

HoverListView::HoverListView(std::unique_ptr<HoverListModel> model)
    : model_(std::move(model)) {
  DCHECK(model_);
  SetLayoutManager(std::make_unique<views::FillLayout>());

  auto item_container = std::make_unique<views::View>();
  item_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
      0 /* betweeen_child_spacing */));

  item_container_ = item_container.get();
  item_container_->AddChildView(std::make_unique<views::Separator>());

  for (const auto item_tag : model_->GetButtonTags()) {
    AppendListItemView(model_->GetItemIcon(item_tag),
                       model_->GetItemText(item_tag),
                       model_->GetDescriptionText(item_tag),
                       model_->IsButtonEnabled(item_tag), item_tag);
  }

  scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
  scroll_view_->SetContents(std::move(item_container));
  scroll_view_->ClipHeightTo(GetPreferredViewHeight(),
                             GetPreferredViewHeight());
}

HoverListView::~HoverListView() = default;

void HoverListView::AppendListItemView(const ui::ImageModel& icon,
                                       std::u16string item_text,
                                       std::u16string description_text,
                                       bool enabled,
                                       int item_tag) {
  // Safe because the hover button is added as a child view owned by `this`,
  // guaranteeing `this` strictly outlives `hover_button` and its callback.
  auto hover_button = CreateHoverButtonForListItem(
      icon, item_text, description_text, enabled,
      base::BindRepeating(&HoverListView::OnListItemSelected,
                          base::Unretained(this), item_tag));

  auto* list_item_view_ptr = hover_button.release();
  item_container_->AddChildViewRaw(list_item_view_ptr);
  auto* separator =
      item_container_->AddChildView(std::make_unique<views::Separator>());
  tags_to_list_item_views_.emplace(
      item_tag, ListItemViews{list_item_view_ptr, separator});
}

void HoverListView::OnListItemSelected(int item_tag, const ui::Event& event) {
  if (GetWidget() && GetWidget()->widget_delegate()) {
    if (views::DialogDelegate* dialog_delegate =
            GetWidget()->widget_delegate()->AsDialogDelegate()) {
      if (views::DialogClientView* dialog_client_view =
              dialog_delegate->GetDialogClientView()) {
        if (dialog_client_view->IsPossiblyUnintendedInteraction(
                event, /*allow_key_events=*/false)) {
          return;
        }
      }
    }
  }
  model_->OnListItemSelected(item_tag);
}

views::Button& HoverListView::GetTopListItemView() const {
  DCHECK(!tags_to_list_item_views_.empty());
  return *tags_to_list_item_views_.begin()->second.item_view;
}

void HoverListView::RequestFocus() {
  if (tags_to_list_item_views_.empty()) {
    return;
  }

  GetTopListItemView().RequestFocus();
}

int HoverListView::GetPreferredViewHeight() const {
  constexpr int kMaxViewHeight = 300;

  // |item_container_| has one separator at the top and list items which
  // contain one separator and one hover button.
  const auto separator_height = views::Separator().GetPreferredSize().height();
  int size = separator_height;
  for (const auto& iter : tags_to_list_item_views_) {
    size +=
        iter.second.item_view->GetPreferredSize().height() + separator_height;
  }
  int reserved_items =
      model_->GetPreferredItemCount() - tags_to_list_item_views_.size();
  if (reserved_items > 0) {
    auto dummy_hover_button = CreateHoverButtonForListItem(
        ui::ImageModel(), std::u16string(), std::u16string(),
        /*enabled=*/true, views::Button::PressedCallback());
    const auto list_item_height =
        separator_height + dummy_hover_button->GetPreferredSize().height();
    size += list_item_height * reserved_items;
  }
  return std::min(kMaxViewHeight, size);
}

BEGIN_METADATA(HoverListView)
ADD_READONLY_PROPERTY_METADATA(int, PreferredViewHeight)
END_METADATA
