// Copyright (c) 2012 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.

#ifndef CHROME_BROWSER_EXTENSIONS_BUNDLE_INSTALLER_H_
#define CHROME_BROWSER_EXTENSIONS_BUNDLE_INSTALLER_H_

#include <stddef.h>

#include <string>
#include <vector>

#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
#include "chrome/browser/extensions/webstore_install_helper.h"
#include "chrome/browser/extensions/webstore_installer.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "extensions/common/extension.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "url/gurl.h"

namespace base {
class DictionaryValue;
}  // namespace base

namespace content {
class WebContents;
}  // namespace content

class Browser;
class Profile;

namespace extensions {

// Manages the installation life cycle for extension bundles.
//
// We install bundles in two steps:
//  1) PromptForApproval: parse manifests and prompt the user
//  2) CompleteInstall: install the CRXs and show confirmation bubble
//
class BundleInstaller : public WebstoreInstallHelper::Delegate,
                        public WebstoreInstaller::Delegate,
                        public chrome::BrowserListObserver {
 public:
  // Auto approve or cancel the permission prompt.
  static void SetAutoApproveForTesting(bool approve);

  // Represents an individual member of the bundle.
  struct Item {
    // Items are in the PENDING state until they've been installed, or the
    // install has failed or been canceled.
    enum State {
      STATE_PENDING,
      STATE_INSTALLED,
      STATE_FAILED
    };

    Item();
    Item(const Item& other);
    ~Item();

    // Gets the localized name, formatted for display in the bubble.
    base::string16 GetNameForDisplay() const;

    std::string id;
    std::string manifest;
    std::string localized_name;
    GURL icon_url;
    SkBitmap icon;
    State state;
  };

  enum ApprovalState {
    APPROVED,
    USER_CANCELED,
    APPROVAL_ERROR
  };

  typedef base::Callback<void(ApprovalState)> ApprovalCallback;

  typedef std::vector<Item> ItemList;

  BundleInstaller(Browser* browser,
                  const std::string& localized_name,
                  const SkBitmap& icon,
                  const std::string& authuser,
                  const std::string& delegated_username,
                  const ItemList& items);
  ~BundleInstaller() override;

  // Returns true if the user has approved the bundle's permissions.
  bool approved() const { return approved_; }

  // Returns the browser window associated with the bundle's installation.
  // Can return null if the browser is closed during the installation.
  Browser* browser() { return browser_; }

  // Gets the items in the given |state|.
  ItemList GetItemsWithState(Item::State state) const;

  // Returns whether there is at least one item with the given |state|.
  bool HasItemWithState(Item::State state) const;

  // Returns the number of items with the given |state|.
  size_t CountItemsWithState(Item::State state) const;

  // Parses the extension manifests and then prompts the user to approve their
  // permissions.
  void PromptForApproval(const ApprovalCallback& callback);

  // If the bundle has been approved, this downloads and installs the member
  // extensions. The download process uses the NavigationController of the
  // specified |web_contents|. When complete, we show a confirmation bubble.
  void CompleteInstall(content::WebContents* web_contents,
                       const base::Closure& callback);

  // We change the headings in the install prompt and installed bubble depending
  // on whether the bundle contains apps, extensions or both. This method gets
  // the correct heading for the items in the specified |state|, or an empty
  // string if no items are in the |state|.
  //   STATE_PENDING   - install prompt
  //   STATE_INSTALLED - installed bubble successful installs list
  //   STATE_FAILED    - installed bubble failed installs list
  base::string16 GetHeadingTextFor(Item::State state) const;

 private:
  typedef std::map<std::string, Item> ItemMap;
  typedef std::map<std::string, linked_ptr<base::DictionaryValue> > ManifestMap;

  // Displays the install bubble for |bundle| on |browser|.
  // Note: this is a platform specific implementation.
  static void ShowInstalledBubble(const BundleInstaller* bundle,
                                  Browser* browser);

  // Parses the manifests using WebstoreInstallHelper.
  void ParseManifests();

  // Prompts the user to install the bundle once we have dummy extensions for
  // all the pending items.
  void ShowPromptIfDoneParsing();

  // Prompts the user to install the bundle.
  void ShowPrompt();

  // Displays the installed bubble once all items have installed or failed.
  void ShowInstalledBubbleIfDone();

  // WebstoreInstallHelper::Delegate implementation:
  void OnWebstoreParseSuccess(const std::string& id,
                              const SkBitmap& icon,
                              base::DictionaryValue* parsed_manifest) override;
  void OnWebstoreParseFailure(const std::string& id,
                              InstallHelperResultCode result_code,
                              const std::string& error_message) override;

  // WebstoreInstaller::Delegate implementation:
  void OnExtensionInstallSuccess(const std::string& id) override;
  void OnExtensionInstallFailure(
      const std::string& id,
      const std::string& error,
      WebstoreInstaller::FailureReason reason) override;

  // chrome::BrowserListObserver implementation:
  void OnBrowserRemoved(Browser* browser) override;

  void OnInstallPromptDone(ExtensionInstallPrompt::Result result);

  // Holds the Extensions used to generate the permission warnings.
  ExtensionList dummy_extensions_;

  // Holds the parsed manifests, indexed by the extension ids.
  ManifestMap parsed_manifests_;

  // True if the user has approved the bundle.
  bool approved_;

  // Holds the bundle's Items, indexed by their ids.
  ItemMap items_;

  // The browser to show the confirmation bubble for.
  Browser* browser_;

  // The bundle's display name.
  std::string name_;

  // The bundle's icon.
  SkBitmap icon_;

  // The authuser query parameter value which should be used with CRX download
  // requests. May be empty.
  std::string authuser_;

  // The display name of the user for which this install happens, in the case
  // of delegated installs. Empty for regular installs.
  std::string delegated_username_;

  // The profile that the bundle should be installed in.
  Profile* profile_;

  // The UI that shows the confirmation prompt.
  scoped_ptr<ExtensionInstallPrompt> install_ui_;

  ApprovalCallback approval_callback_;
  base::Closure install_callback_;

  base::WeakPtrFactory<BundleInstaller> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(BundleInstaller);
};

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_BUNDLE_INSTALLER_H_