// 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.

#include "chrome/browser/plugins/plugin_installer.h"

#include <utility>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/process/process.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/plugins/plugin_installer_observer.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_url_parameters.h"
#include "content/public/browser/web_contents.h"

using content::DownloadItem;

PluginInstaller::PluginInstaller()
    : state_(INSTALLER_STATE_IDLE),
      strong_observer_count_(0) {
}

PluginInstaller::~PluginInstaller() {
}

void PluginInstaller::OnDownloadUpdated(DownloadItem* download) {
  DownloadItem::DownloadState state = download->GetState();
  switch (state) {
    case DownloadItem::IN_PROGRESS:
      return;
    case DownloadItem::COMPLETE: {
      DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
      state_ = INSTALLER_STATE_IDLE;
      FOR_EACH_OBSERVER(PluginInstallerObserver, observers_,
                        DownloadFinished());
      break;
    }
    case DownloadItem::CANCELLED: {
      DownloadCancelled();
      break;
    }
    case DownloadItem::INTERRUPTED: {
      content::DownloadInterruptReason reason = download->GetLastReason();
      DownloadError(content::DownloadInterruptReasonToString(reason));
      break;
    }
    case DownloadItem::MAX_DOWNLOAD_STATE: {
      NOTREACHED();
      return;
    }
  }
  download->RemoveObserver(this);
}

void PluginInstaller::OnDownloadDestroyed(DownloadItem* download) {
  DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
  state_ = INSTALLER_STATE_IDLE;
  download->RemoveObserver(this);
}

void PluginInstaller::AddObserver(PluginInstallerObserver* observer) {
  strong_observer_count_++;
  observers_.AddObserver(observer);
}

void PluginInstaller::RemoveObserver(PluginInstallerObserver* observer) {
  strong_observer_count_--;
  observers_.RemoveObserver(observer);
  if (strong_observer_count_ == 0) {
    FOR_EACH_OBSERVER(WeakPluginInstallerObserver, weak_observers_,
                      OnlyWeakObserversLeft());
  }
}

void PluginInstaller::AddWeakObserver(WeakPluginInstallerObserver* observer) {
  weak_observers_.AddObserver(observer);
}

void PluginInstaller::RemoveWeakObserver(
    WeakPluginInstallerObserver* observer) {
  weak_observers_.RemoveObserver(observer);
}

void PluginInstaller::StartInstalling(const GURL& plugin_url,
                                      content::WebContents* web_contents) {
  content::DownloadManager* download_manager =
      content::BrowserContext::GetDownloadManager(
          web_contents->GetBrowserContext());
  StartInstallingWithDownloadManager(
      plugin_url, web_contents, download_manager);
}

void PluginInstaller::StartInstallingWithDownloadManager(
    const GURL& plugin_url,
    content::WebContents* web_contents,
    content::DownloadManager* download_manager) {
  DCHECK_EQ(INSTALLER_STATE_IDLE, state_);
  state_ = INSTALLER_STATE_DOWNLOADING;
  FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadStarted());
  scoped_ptr<content::DownloadUrlParameters> download_parameters(
      content::DownloadUrlParameters::FromWebContents(web_contents,
                                                      plugin_url));
  download_parameters->set_callback(
      base::Bind(&PluginInstaller::DownloadStarted, base::Unretained(this)));
  RecordDownloadSource(DOWNLOAD_INITIATED_BY_PLUGIN_INSTALLER);
  download_manager->DownloadUrl(std::move(download_parameters));
}

void PluginInstaller::DownloadStarted(
    content::DownloadItem* item,
    content::DownloadInterruptReason interrupt_reason) {
  if (interrupt_reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) {
    std::string msg = base::StringPrintf(
        "Error %d: %s",
        interrupt_reason,
        content::DownloadInterruptReasonToString(interrupt_reason).c_str());
    DownloadError(msg);
    return;
  }
  item->SetOpenWhenComplete(true);
  item->AddObserver(this);
}

void PluginInstaller::OpenDownloadURL(const GURL& plugin_url,
                                      content::WebContents* web_contents) {
  DCHECK_EQ(INSTALLER_STATE_IDLE, state_);
  web_contents->OpenURL(content::OpenURLParams(
      plugin_url,
      content::Referrer(web_contents->GetURL(),
                        blink::WebReferrerPolicyDefault),
      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false));
  FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadFinished());
}

void PluginInstaller::DownloadError(const std::string& msg) {
  DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
  state_ = INSTALLER_STATE_IDLE;
  FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadError(msg));
}

void PluginInstaller::DownloadCancelled() {
  DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
  state_ = INSTALLER_STATE_IDLE;
  FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadCancelled());
}