// Copyright (c) 2011 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/extensions/extension_disabled_infobar_delegate.h"

#include <string>

#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_resource.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_details.h"
#include "content/common/notification_registrar.h"
#include "content/common/notification_source.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

// ExtensionDisabledDialogDelegate --------------------------------------------

class ExtensionDisabledDialogDelegate
    : public ExtensionInstallUI::Delegate,
      public base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate> {
 public:
  ExtensionDisabledDialogDelegate(Profile* profile,
                                  ExtensionService* service,
                                  const Extension* extension);

 private:
  friend class base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate>;

  virtual ~ExtensionDisabledDialogDelegate();

  // ExtensionInstallUI::Delegate:
  virtual void InstallUIProceed();
  virtual void InstallUIAbort();

  // The UI for showing the install dialog when enabling.
  scoped_ptr<ExtensionInstallUI> install_ui_;

  ExtensionService* service_;
  const Extension* extension_;
};

ExtensionDisabledDialogDelegate::ExtensionDisabledDialogDelegate(
    Profile* profile,
    ExtensionService* service,
    const Extension* extension)
    : service_(service), extension_(extension) {
  AddRef();  // Balanced in Proceed or Abort.

  install_ui_.reset(new ExtensionInstallUI(profile));
  install_ui_->ConfirmReEnable(this, extension_);
}

ExtensionDisabledDialogDelegate::~ExtensionDisabledDialogDelegate() {
}

void ExtensionDisabledDialogDelegate::InstallUIProceed() {
  service_->GrantPermissionsAndEnableExtension(extension_);
  Release();
}

void ExtensionDisabledDialogDelegate::InstallUIAbort() {
  ExtensionService::RecordPermissionMessagesHistogram(
      extension_, "Extensions.Permissions_ReEnableCancel");

  // Do nothing. The extension will remain disabled.
  Release();
}


// ExtensionDisabledInfobarDelegate -------------------------------------------

class ExtensionDisabledInfobarDelegate : public ConfirmInfoBarDelegate,
                                         public NotificationObserver {
 public:
  ExtensionDisabledInfobarDelegate(TabContents* tab_contents,
                                   ExtensionService* service,
                                   const Extension* extension);

 private:
  virtual ~ExtensionDisabledInfobarDelegate();

  // ConfirmInfoBarDelegate:
  virtual void InfoBarClosed();
  virtual string16 GetMessageText() const;
  virtual int GetButtons() const;
  virtual string16 GetButtonLabel(InfoBarButton button) const;
  virtual bool Accept();

  // NotificationObserver:
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  NotificationRegistrar registrar_;
  TabContents* tab_contents_;
  ExtensionService* service_;
  const Extension* extension_;
};

ExtensionDisabledInfobarDelegate::ExtensionDisabledInfobarDelegate(
    TabContents* tab_contents,
    ExtensionService* service,
    const Extension* extension)
    : ConfirmInfoBarDelegate(tab_contents),
      tab_contents_(tab_contents),
      service_(service),
      extension_(extension) {
  // The user might re-enable the extension in other ways, so watch for that.
  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
                 Source<Profile>(service->profile()));
  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                 Source<Profile>(service->profile()));
}

ExtensionDisabledInfobarDelegate::~ExtensionDisabledInfobarDelegate() {
}

void ExtensionDisabledInfobarDelegate::InfoBarClosed() {
  delete this;
}

string16 ExtensionDisabledInfobarDelegate::GetMessageText() const {
  return l10n_util::GetStringFUTF16(extension_->is_app() ?
      IDS_APP_DISABLED_INFOBAR_LABEL : IDS_EXTENSION_DISABLED_INFOBAR_LABEL,
      UTF8ToUTF16(extension_->name()));
}

int ExtensionDisabledInfobarDelegate::GetButtons() const {
  return BUTTON_OK;
}

string16 ExtensionDisabledInfobarDelegate::GetButtonLabel(
    InfoBarButton button) const {
  DCHECK_EQ(BUTTON_OK, button);
  return l10n_util::GetStringUTF16(
      IDS_EXTENSION_DISABLED_INFOBAR_ENABLE_BUTTON);
}

bool ExtensionDisabledInfobarDelegate::Accept() {
  // This object manages its own lifetime.
  new ExtensionDisabledDialogDelegate(tab_contents_->profile(), service_,
                                      extension_);
  return true;
}

void ExtensionDisabledInfobarDelegate::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  // TODO(mpcomplete): RemoveInfoBar doesn't seem to always result in us getting
  // deleted.
  const Extension* extension;
  if (type.value == NotificationType::EXTENSION_LOADED) {
    extension = Details<const Extension>(details).ptr();
  } else {
    DCHECK_EQ(NotificationType::EXTENSION_UNLOADED, type.value);
    UnloadedExtensionInfo* info = Details<UnloadedExtensionInfo>(details).ptr();
    extension = (info->reason == UnloadedExtensionInfo::DISABLE) ?
        info->extension : NULL;
  }
  if (extension == extension_)
    tab_contents_->RemoveInfoBar(this);
}


// Globals --------------------------------------------------------------------

void ShowExtensionDisabledUI(ExtensionService* service, Profile* profile,
                             const Extension* extension) {
  Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
  if (!browser)
    return;

  TabContents* tab_contents = browser->GetSelectedTabContents();
  if (!tab_contents)
    return;

  tab_contents->AddInfoBar(new ExtensionDisabledInfobarDelegate(
      tab_contents, service, extension));
}

void ShowExtensionDisabledDialog(ExtensionService* service, Profile* profile,
                                 const Extension* extension) {
  // This object manages its own lifetime.
  new ExtensionDisabledDialogDelegate(profile, service, extension);
}