// 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/extensions/bookmark_app_bubble_view.h"

#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/constants.h"
#include "grit/components_strings.h"
#include "ui/accessibility/ax_view_state.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"

#if defined(OS_WIN)
#include "base/win/shortcut.h"
#endif  // defined(OS_WIN)

using views::ColumnSet;
using views::GridLayout;

namespace {

// Minimum width of the the bubble.
const int kMinBubbleWidth = 300;
// Minimum width of the the textfield.
const int kMinTextfieldWidth = 200;
// Size of the icon.
const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM;

class WebAppInfoImageSource : public gfx::ImageSkiaSource {
 public:
  WebAppInfoImageSource(int dip_size, const WebApplicationInfo& info)
      : dip_size_(dip_size), info_(info) {}
  ~WebAppInfoImageSource() override {}

 private:
  gfx::ImageSkiaRep GetImageForScale(float scale) override {
    int size = base::saturated_cast<int>(dip_size_ * scale);
    for (const auto& icon_info : info_.icons) {
      if (icon_info.width == size) {
        return gfx::ImageSkiaRep(icon_info.data, scale);
      }
    }
    return gfx::ImageSkiaRep();
  }

  int dip_size_;
  WebApplicationInfo info_;
};

}  // namespace

BookmarkAppBubbleView::~BookmarkAppBubbleView() {
}

// static
void BookmarkAppBubbleView::ShowBubble(
    views::View* anchor_view,
    const WebApplicationInfo& web_app_info,
    const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) {
  // |bookmark_app_bubble| becomes owned by the BubbleDelegateView through the
  // views system, and is freed when the BubbleDelegateView is closed and
  // subsequently destroyed.
  BookmarkAppBubbleView* bookmark_app_bubble =
      new BookmarkAppBubbleView(anchor_view, web_app_info, callback);
  views::BubbleDelegateView::CreateBubble(bookmark_app_bubble)->Show();
  // Select the entire title textfield contents when the bubble is first shown.
  bookmark_app_bubble->title_tf_->SelectAll(true);
  bookmark_app_bubble->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
}

BookmarkAppBubbleView::BookmarkAppBubbleView(
    views::View* anchor_view,
    const WebApplicationInfo& web_app_info,
    const BrowserWindow::ShowBookmarkAppBubbleCallback& callback)
    : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
      web_app_info_(web_app_info),
      user_accepted_(false),
      callback_(callback),
      add_button_(NULL),
      cancel_button_(NULL),
      open_as_window_checkbox_(NULL),
      title_tf_(NULL) {
  const SkColor background_color = GetNativeTheme()->GetSystemColor(
      ui::NativeTheme::kColorId_DialogBackground);
  set_arrow(views::BubbleBorder::TOP_CENTER);
  set_color(background_color);
  set_background(views::Background::CreateSolidBackground(background_color));
  set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0));
}

void BookmarkAppBubbleView::Init() {
  views::Label* title_label = new views::Label(
      l10n_util::GetStringUTF16(TitleStringId()));
  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
  title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);

  add_button_ =
      new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_ADD));
  add_button_->SetStyle(views::Button::STYLE_BUTTON);
  add_button_->SetIsDefault(true);

  cancel_button_ =
      new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
  cancel_button_->SetStyle(views::Button::STYLE_BUTTON);

  GridLayout* layout = new GridLayout(this);
  SetLayoutManager(layout);

  // Column sets used in the layout of the bubble.
  enum ColumnSetID {
    TITLE_COLUMN_SET_ID,
    TITLE_TEXT_COLUMN_SET_ID,
    CONTENT_COLUMN_SET_ID
  };

  // The column layout used for the title and checkbox.
  ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
  cs->AddColumn(
      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);

  // The column layout used for the icon and text box.
  cs = layout->AddColumnSet(TITLE_TEXT_COLUMN_SET_ID);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
  cs->AddColumn(GridLayout::LEADING,
                GridLayout::CENTER,
                0,
                GridLayout::USE_PREF,
                0,
                0);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
  cs->AddColumn(GridLayout::FILL,
                GridLayout::CENTER,
                1,
                GridLayout::USE_PREF,
                0,
                kMinTextfieldWidth);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);

  // The column layout used for the row with buttons.
  cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
  cs->AddColumn(
      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
  cs->AddColumn(
      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
  cs->AddColumn(
      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);

  layout->StartRow(0, TITLE_COLUMN_SET_ID);
  layout->AddView(title_label);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  layout->StartRow(0, TITLE_TEXT_COLUMN_SET_ID);
  icon_image_view_ = new views::ImageView();

  gfx::Size image_size(kIconSize, kIconSize);
  gfx::ImageSkia image(new WebAppInfoImageSource(kIconSize, web_app_info_),
                       image_size);
  icon_image_view_->SetImageSize(image_size);
  icon_image_view_->SetImage(image);
  layout->AddView(icon_image_view_);

  title_tf_ = new views::Textfield();
  title_tf_->SetText(web_app_info_.title);
  title_tf_->SetAccessibleName(l10n_util::GetStringUTF16(
      IDS_BOOKMARK_APP_AX_BUBBLE_NAME_LABEL));
  title_tf_->set_controller(this);
  layout->AddView(title_tf_);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  layout->StartRow(0, CONTENT_COLUMN_SET_ID);
  open_as_window_checkbox_ = new views::Checkbox(
      l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW));
  open_as_window_checkbox_->SetChecked(web_app_info_.open_as_window);
  layout->AddView(open_as_window_checkbox_);
  layout->AddView(add_button_);
  layout->AddView(cancel_button_);
  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);

  AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
  UpdateAddButtonState();
}

views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() {
  return title_tf_;
}

void BookmarkAppBubbleView::WindowClosing() {
  callback_.Run(user_accepted_, web_app_info_);
}

bool BookmarkAppBubbleView::AcceleratorPressed(
    const ui::Accelerator& accelerator) {
  if (accelerator.key_code() == ui::VKEY_RETURN) {
    HandleButtonPressed(add_button_);
  }

  return BubbleDelegateView::AcceleratorPressed(accelerator);
}

void BookmarkAppBubbleView::GetAccessibleState(ui::AXViewState* state) {
  views::BubbleDelegateView::GetAccessibleState(state);
  state->name = l10n_util::GetStringUTF16(TitleStringId());
}

gfx::Size BookmarkAppBubbleView::GetMinimumSize() const {
  gfx::Size size(views::BubbleDelegateView::GetPreferredSize());
  size.SetToMax(gfx::Size(kMinBubbleWidth, 0));
  return size;
}

void BookmarkAppBubbleView::ButtonPressed(views::Button* sender,
                                          const ui::Event& event) {
  HandleButtonPressed(sender);
}

void BookmarkAppBubbleView::ContentsChanged(
    views::Textfield* sender,
    const base::string16& new_contents) {
  DCHECK_EQ(title_tf_, sender);
  UpdateAddButtonState();
}

void BookmarkAppBubbleView::HandleButtonPressed(views::Button* sender) {
  if (sender == add_button_) {
    user_accepted_ = true;
    web_app_info_.title = GetTrimmedTitle();
    web_app_info_.open_as_window = open_as_window_checkbox_->checked();
  }

  GetWidget()->Close();
}

void BookmarkAppBubbleView::UpdateAddButtonState() {
  add_button_->SetEnabled(!GetTrimmedTitle().empty());
}

int BookmarkAppBubbleView::TitleStringId() {
#if defined(USE_ASH)
  return IDS_ADD_TO_SHELF_BUBBLE_TITLE;
#else
  return IDS_ADD_TO_DESKTOP_BUBBLE_TITLE;
#endif
}

base::string16 BookmarkAppBubbleView::GetTrimmedTitle() {
  base::string16 title(title_tf_->text());
  base::TrimWhitespace(title, base::TRIM_ALL, &title);
  return title;
}