// Copyright 2014 The Chromium Authors. All rights reserved.
// 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/autofill/password_generation_popup_view_views.h"

#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/browser/ui/autofill/password_generation_popup_controller.h"
#include "chrome/browser/ui/autofill/popup_constants.h"
#include "ui/accessibility/ax_view_state.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icons_public.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"

namespace autofill {

namespace {

// The amount of whitespace that is present when there is no padding. Used
// to get the proper spacing in the help section.
const int kHelpVerticalOffset = 5;

// Wrapper around just the text portions of the generation UI (password and
// prompting text).
class PasswordTextBox : public views::View {
 public:
  PasswordTextBox() {}
  ~PasswordTextBox() override {}

  // |suggestion_text| prompts the user to select the password,
  // |generated_password| is the generated password, and |font_list| is the font
  // used for all text in this class.
  void Init(const base::string16& suggestion_text,
            const base::string16& generated_password,
            const gfx::FontList& font_list) {
    views::BoxLayout* box_layout = new views::BoxLayout(
        views::BoxLayout::kVertical, 0, 12, 5);
    box_layout->set_main_axis_alignment(
        views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
    SetLayoutManager(box_layout);

    views::Label* suggestion_label = new views::Label(
        suggestion_text, font_list.DeriveWithStyle(gfx::Font::BOLD));
    suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    suggestion_label->SetEnabledColor(
        PasswordGenerationPopupView::kPasswordTextColor);
    AddChildView(suggestion_label);

    views::Label* password_label =
        new views::Label(generated_password, font_list);
    password_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    password_label->SetEnabledColor(
        PasswordGenerationPopupView::kPasswordTextColor);
    AddChildView(password_label);
  }

  // views::View:
  bool CanProcessEventsWithinSubtree() const override {
    // Send events to the parent view for handling.
    return false;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(PasswordTextBox);
};

}  // namespace

// Class that shows the generated password and associated UI (currently a key
// image and some explanatory text).
class PasswordGenerationPopupViewViews::PasswordBox : public views::View {
 public:
  PasswordBox() {}
  ~PasswordBox() override {}

  // |password| is the generated password, |suggestion| is the text prompting
  // the user to select the password, and |font_list| is the font used for all
  // the text.
  void Init(const base::string16& password,
            const base::string16& suggestion,
            const gfx::FontList& font_list) {
    views::BoxLayout* box_layout = new views::BoxLayout(
        views::BoxLayout::kHorizontal,
        PasswordGenerationPopupController::kHorizontalPadding, 0,
        PasswordGenerationPopupController::kHorizontalPadding);
    box_layout->set_main_axis_alignment(
        views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
    SetLayoutManager(box_layout);

    views::ImageView* key_image = new views::ImageView();
    key_image->SetImage(gfx::CreateVectorIcon(gfx::VectorIconId::AUTOLOGIN, 32,
                                              gfx::kChromeIconGrey));
    AddChildView(key_image);

    PasswordTextBox* password_text_box = new PasswordTextBox();
    password_text_box->Init(suggestion, password, font_list);
    AddChildView(password_text_box);
  }

  // views::View:
  bool CanProcessEventsWithinSubtree() const override {
    // Send events to the parent view for handling.
    return false;
  }

 private:

  DISALLOW_COPY_AND_ASSIGN(PasswordBox);
};

PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews(
    PasswordGenerationPopupController* controller,
    views::Widget* parent_widget)
    : AutofillPopupBaseView(controller, parent_widget),
      password_view_(NULL),
      font_list_(ResourceBundle::GetSharedInstance().GetFontList(
          ResourceBundle::SmallFont)),
      controller_(controller) {
  if (controller_->display_password())
    CreatePasswordView();

  help_label_ = new views::StyledLabel(controller_->HelpText(), this);
  help_label_->SetBaseFontList(font_list_);
  help_label_->SetLineHeight(20);
  views::StyledLabel::RangeStyleInfo default_style;
  default_style.color = kExplanatoryTextColor;
  help_label_->SetDefaultStyle(default_style);

  views::StyledLabel::RangeStyleInfo link_style =
      views::StyledLabel::RangeStyleInfo::CreateForLink();
  link_style.disable_line_wrapping = false;
  help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style);

  help_label_->set_background(
      views::Background::CreateSolidBackground(
          kExplanatoryTextBackgroundColor));
  help_label_->SetBorder(views::Border::CreateEmptyBorder(
      PasswordGenerationPopupController::kHelpVerticalPadding -
      kHelpVerticalOffset,
      PasswordGenerationPopupController::kHorizontalPadding,
      PasswordGenerationPopupController::kHelpVerticalPadding -
      kHelpVerticalOffset,
      PasswordGenerationPopupController::kHorizontalPadding));
  AddChildView(help_label_);

  set_background(views::Background::CreateSolidBackground(kPopupBackground));
}

PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {}

void PasswordGenerationPopupViewViews::CreatePasswordView() {
  if (password_view_)
    return;

  password_view_ = new PasswordBox();
  password_view_->Init(controller_->password(),
                       controller_->SuggestedText(),
                       font_list_);
  password_view_->SetPosition(gfx::Point(kPopupBorderThickness,
                                         kPopupBorderThickness));
  password_view_->SizeToPreferredSize();
  AddChildView(password_view_);
}

gfx::Size PasswordGenerationPopupViewViews::GetPreferredSizeOfPasswordView() {
  int height = kPopupBorderThickness;
  if (controller_->display_password()) {
    // Add divider height as well.
    height +=
        PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1;
  }
  int width = controller_->GetMinimumWidth();
  int popup_width = width - 2 * kPopupBorderThickness;
  height += help_label_->GetHeightForWidth(popup_width);
  return gfx::Size(width, height + kPopupBorderThickness);
}

void PasswordGenerationPopupViewViews::Show() {
  DoShow();
}

void PasswordGenerationPopupViewViews::Hide() {
  // The controller is no longer valid after it hides us.
  controller_ = NULL;

  DoHide();
}

void PasswordGenerationPopupViewViews::UpdateBoundsAndRedrawPopup() {
  DoUpdateBoundsAndRedrawPopup();
}

void PasswordGenerationPopupViewViews::PasswordSelectionUpdated() {
  if (!password_view_)
    return;

  if (controller_->password_selected())
    NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);

  password_view_->set_background(
      views::Background::CreateSolidBackground(
          controller_->password_selected() ?
          kHoveredBackgroundColor :
          kPopupBackground));
}

void PasswordGenerationPopupViewViews::Layout() {
  // Need to leave room for the border.
  int y = kPopupBorderThickness;
  int popup_width = bounds().width() - 2 * kPopupBorderThickness;
  if (controller_->display_password()) {
    // Currently the UI can change from not offering a password to offering
    // a password (e.g. the user is editing a generated password and deletes
    // it), but it can't change the other way around.
    CreatePasswordView();
    password_view_->SetBounds(
        kPopupBorderThickness,
        y,
        popup_width,
        PasswordGenerationPopupController::kPopupPasswordSectionHeight);
    divider_bounds_ =
        gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(),
                  popup_width, 1);
    y = divider_bounds_.bottom();
  }

  help_label_->SetBounds(kPopupBorderThickness, y, popup_width,
                         help_label_->GetHeightForWidth(popup_width));
}

void PasswordGenerationPopupViewViews::OnPaint(gfx::Canvas* canvas) {
  if (!controller_)
    return;

  // Draw border and background.
  views::View::OnPaint(canvas);

  // Divider line needs to be drawn after OnPaint() otherwise the background
  // will overwrite the divider.
  if (password_view_)
    canvas->FillRect(divider_bounds_, kDividerColor);
}

void PasswordGenerationPopupViewViews::StyledLabelLinkClicked(
    views::StyledLabel* label,
    const gfx::Range& range,
    int event_flags) {
  controller_->OnSavedPasswordsLinkClicked();
}

bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds(
    const gfx::Point& point) {
  if (password_view_)
    return password_view_->bounds().Contains(point);
  return false;
}

PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
    PasswordGenerationPopupController* controller) {
  views::Widget* observing_widget =
      views::Widget::GetTopLevelWidgetForNativeView(
          controller->container_view());

  // If the top level widget can't be found, cancel the popup since we can't
  // fully set it up.
  if (!observing_widget)
    return NULL;

  return new PasswordGenerationPopupViewViews(controller, observing_widget);
}

void PasswordGenerationPopupViewViews::GetAccessibleState(
    ui::AXViewState* state) {
  state->name = controller_->SuggestedText();
  state->role = ui::AX_ROLE_MENU_ITEM;
}

}  // namespace autofill