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

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/prefs/pref_service.h"
#include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"

#if BUILDFLAG(IS_WIN)
#include "base/win/shortcut.h"
#include "chrome/installer/util/taskbar_util.h"
#endif  // BUILDFLAG(IS_WIN)

namespace chrome {

void ShowCreateChromeAppShortcutsDialog(
    gfx::NativeWindow parent_window,
    Profile* profile,
    const extensions::Extension* app,
    base::OnceCallback<void(bool)> close_callback) {
  constrained_window::CreateBrowserModalDialogViews(
      new CreateChromeApplicationShortcutView(profile, app,
                                              std::move(close_callback)),
      parent_window)
      ->Show();
}

void ShowCreateChromeAppShortcutsDialog(
    gfx::NativeWindow parent_window,
    Profile* profile,
    const std::string& web_app_id,
    base::OnceCallback<void(bool)> close_callback) {
  constrained_window::CreateBrowserModalDialogViews(
      new CreateChromeApplicationShortcutView(profile, web_app_id,
                                              std::move(close_callback)),
      parent_window)
      ->Show();
}

}  // namespace chrome

CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
    Profile* profile,
    const extensions::Extension* app,
    base::OnceCallback<void(bool)> close_callback)
    : CreateChromeApplicationShortcutView(profile,
                                          /*is_extension=*/true,
                                          std::move(close_callback)) {
  // Get shortcut and icon information; needed for creating the shortcut.
  web_app::GetShortcutInfoForApp(
      app, profile,
      base::BindRepeating(&CreateChromeApplicationShortcutView::OnAppInfoLoaded,
                          weak_ptr_factory_.GetWeakPtr()));
}

CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
    Profile* profile,
    const std::string& web_app_id,
    base::OnceCallback<void(bool)> close_callback)
    : CreateChromeApplicationShortcutView(profile,
                                          /*is_extension=*/false,
                                          std::move(close_callback)) {
  web_app::WebAppProvider* provider =
      web_app::WebAppProvider::GetForWebApps(profile);
  provider->os_integration_manager().GetShortcutInfoForAppFromRegistrar(
      web_app_id,
      base::BindRepeating(&CreateChromeApplicationShortcutView::OnAppInfoLoaded,
                          weak_ptr_factory_.GetWeakPtr()));
}

CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
    Profile* profile,
    bool is_extension,
    base::OnceCallback<void(bool)> close_callback)
    : profile_(profile),
      prefs_(profile->GetPrefs()),
      is_extension_(is_extension),
      close_callback_(std::move(close_callback)) {
  SetModalType(ui::mojom::ModalType::kWindow);
  SetButtonLabel(ui::mojom::DialogButton::kOk,
                 l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_COMMIT));
  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
      views::DialogContentType::kText, views::DialogContentType::kText));
  SetAcceptCallback(
      base::BindOnce(&CreateChromeApplicationShortcutView::OnDialogAccepted,
                     base::Unretained(this)));
  auto canceled = [](CreateChromeApplicationShortcutView* dialog) {
    if (!dialog->close_callback_.is_null()) {
      std::move(dialog->close_callback_).Run(false);
    }
  };
  SetCancelCallback(base::BindOnce(canceled, base::Unretained(this)));
  SetCloseCallback(base::BindOnce(canceled, base::Unretained(this)));
  InitControls();
}

CreateChromeApplicationShortcutView::~CreateChromeApplicationShortcutView() =
    default;

void CreateChromeApplicationShortcutView::InitControls() {
  auto create_shortcuts_label = std::make_unique<views::Label>(
      l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_LABEL));
  create_shortcuts_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  create_shortcuts_label->SetMultiLine(true);

  std::unique_ptr<views::Checkbox> desktop_check_box = AddCheckbox(
      l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX),
      prefs::kWebAppCreateOnDesktop);

  std::unique_ptr<views::Checkbox> menu_check_box;
  std::unique_ptr<views::Checkbox> pin_to_taskbar_checkbox;

#if BUILDFLAG(IS_WIN)
  menu_check_box = AddCheckbox(
      l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_START_MENU_CHKBOX),
      prefs::kWebAppCreateInAppsMenu);

  // Only include the pin-to-taskbar option when running on versions of Windows
  // that support pinning.
  if (CanPinShortcutToTaskbar()) {
    pin_to_taskbar_checkbox =
        AddCheckbox(l10n_util::GetStringUTF16(IDS_PIN_TO_TASKBAR_CHKBOX),
                    prefs::kWebAppCreateInQuickLaunchBar);
  }
#elif BUILDFLAG(IS_POSIX)
  menu_check_box =
      AddCheckbox(l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_MENU_CHKBOX),
                  prefs::kWebAppCreateInAppsMenu);
#endif

  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();

  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
  AddChildView(std::move(create_shortcuts_label));
  desktop_check_box_ = AddChildView(std::move(desktop_check_box));
  if (menu_check_box) {
    menu_check_box_ = AddChildView(std::move(menu_check_box));
  }
  if (pin_to_taskbar_checkbox) {
    quick_launch_check_box_ = AddChildView(std::move(pin_to_taskbar_checkbox));
  }
}

gfx::Size CreateChromeApplicationShortcutView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  static const int kDialogWidth = 360;
  int height =
      GetLayoutManager()->GetPreferredHeightForWidth(this, kDialogWidth);
  return gfx::Size(kDialogWidth, height);
}

bool CreateChromeApplicationShortcutView::IsDialogButtonEnabled(
    ui::mojom::DialogButton button) const {
  if (button != ui::mojom::DialogButton::kOk) {
    return true;  // It's always possible to cancel out of creating a shortcut.
  }

  if (!shortcut_info_) {
    return false;  // Dialog's not ready because app info hasn't been loaded.
  }

  // One of the three location checkboxes must be checked:
  return desktop_check_box_->GetChecked() ||
         (menu_check_box_ && menu_check_box_->GetChecked()) ||
         (quick_launch_check_box_ && quick_launch_check_box_->GetChecked());
}

std::u16string CreateChromeApplicationShortcutView::GetWindowTitle() const {
  return l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_TITLE);
}

void CreateChromeApplicationShortcutView::OnDialogAccepted() {
  DCHECK(IsDialogButtonEnabled(ui::mojom::DialogButton::kOk));

  if (!close_callback_.is_null()) {
    std::move(close_callback_).Run(/*success=*/shortcut_info_ != nullptr);
  }

  // Shortcut can't be created because app info hasn't been loaded.
  if (!shortcut_info_) {
    return;
  }

  web_app::ShortcutLocations creation_locations;
  creation_locations.on_desktop = desktop_check_box_->GetChecked();
  if (menu_check_box_ && menu_check_box_->GetChecked()) {
    creation_locations.applications_menu_location =
        web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
  }

#if BUILDFLAG(IS_WIN)
  creation_locations.in_quick_launch_bar =
      quick_launch_check_box_ && quick_launch_check_box_->GetChecked();
#elif BUILDFLAG(IS_POSIX)
  // Create shortcut in Mac dock or as Linux (gnome/kde) application launcher
  // are not implemented yet.
  creation_locations.in_quick_launch_bar = false;
#endif

  // If the dialog has been triggered from a web_app, then we need to perform OS
  // integration using sub managers so that shortcuts can be properly added,
  // updated or deleted. Otherwise, shortcuts created need not be tracked as
  // they will not be tied to an app_id.
  if (!shortcut_info_->app_id.empty() && !is_extension_) {
    auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
    CHECK(provider);
    provider->scheduler().SynchronizeOsIntegration(
        shortcut_info_->app_id, base::DoNothing(),
        web_app::ConvertShortcutLocationsToSynchronizeOptions(
            creation_locations, web_app::SHORTCUT_CREATION_BY_USER),
        /*upgrade_to_fully_installed_if_installed=*/true);
  } else {
    web_app::CreateShortcutsWithInfo(web_app::SHORTCUT_CREATION_BY_USER,
                                     creation_locations, base::DoNothing(),
                                     std::move(shortcut_info_));
  }
}

std::unique_ptr<views::Checkbox>
CreateChromeApplicationShortcutView::AddCheckbox(const std::u16string& text,
                                                 const std::string& pref_path) {
  auto checkbox =
      std::make_unique<views::Checkbox>(text, views::Button::PressedCallback());
  checkbox->SetCallback(base::BindRepeating(
      &CreateChromeApplicationShortcutView::CheckboxPressed,
      base::Unretained(this), pref_path, base::Unretained(checkbox.get())));
  checkbox->SetChecked(prefs_->GetBoolean(pref_path));
  return checkbox;
}

void CreateChromeApplicationShortcutView::CheckboxPressed(
    std::string pref_path,
    views::Checkbox* checkbox) {
  prefs_->SetBoolean(pref_path, checkbox->GetChecked());
  DialogModelChanged();
}

void CreateChromeApplicationShortcutView::OnAppInfoLoaded(
    std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
  // GetShortcutInfoForApp request may return nullptr |shortcut_info| to this
  // callback if web app was uninstalled during that asynchronous request.
  shortcut_info_ = std::move(shortcut_info);
  // This may cause there to be shortcut info when there was none before, so
  // make sure the accept button gets enabled.
  DialogModelChanged();
}

BEGIN_METADATA(CreateChromeApplicationShortcutView)
END_METADATA
