// Copyright 2019 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/download/deferred_client_wrapper.h"

#include <vector>

#include "base/functional/bind.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/transition_manager/full_browser_transition_manager.h"
#include "components/download/public/background_service/clients.h"
#include "components/download/public/background_service/download_metadata.h"
#include "components/keyed_service/core/simple_factory_key.h"

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/android/startup_bridge.h"
#endif

namespace download {

DeferredClientWrapper::DeferredClientWrapper(ClientFactory client_factory,
                                             SimpleFactoryKey* key)
    : client_factory_(std::move(client_factory)), key_(key) {
#if BUILDFLAG(IS_ANDROID)
  full_browser_requested_ = false;
#endif

  FullBrowserTransitionManager::Get()->RegisterCallbackOnProfileCreation(
      key_, base::BindOnce(&DeferredClientWrapper::InflateClient,
                           weak_ptr_factory_.GetWeakPtr()));
#if !BUILDFLAG(IS_ANDROID)
  // On non-android platforms we can only be running in full browser mode. In
  // full browser mode, FullBrowserTransitionManager synchronously calls the
  // callback when it is registered.
  DCHECK(wrapped_client_);
#endif
}

DeferredClientWrapper::~DeferredClientWrapper() = default;

void DeferredClientWrapper::OnServiceInitialized(
    bool state_lost,
    const std::vector<DownloadMetaData>& downloads) {
  base::OnceClosure callback =
      base::BindOnce(&DeferredClientWrapper::ForwardOnServiceInitialized,
                     weak_ptr_factory_.GetWeakPtr(), state_lost, downloads);
  deferred_closures_.push_back(std::move(callback));

  bool force_inflate = downloads.size() > 0 || state_lost;
  RunDeferredClosures(force_inflate);
}

void DeferredClientWrapper::OnServiceUnavailable() {
  base::OnceClosure callback =
      base::BindOnce(&DeferredClientWrapper::ForwardOnServiceUnavailable,
                     weak_ptr_factory_.GetWeakPtr());
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(false /*force_inflate*/);
}

void DeferredClientWrapper::OnDownloadStarted(
    const std::string& guid,
    const std::vector<GURL>& url_chain,
    const scoped_refptr<const net::HttpResponseHeaders>& headers) {
  base::OnceClosure callback =
      base::BindOnce(&DeferredClientWrapper::ForwardOnDownloadStarted,
                     weak_ptr_factory_.GetWeakPtr(), guid, url_chain, headers);
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(true /*force_inflate*/);
}

void DeferredClientWrapper::OnDownloadUpdated(const std::string& guid,
                                              uint64_t bytes_uploaded,
                                              uint64_t bytes_downloaded) {
  base::OnceClosure callback = base::BindOnce(
      &DeferredClientWrapper::ForwardOnDownloadUpdated,
      weak_ptr_factory_.GetWeakPtr(), guid, bytes_uploaded, bytes_downloaded);
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(true /*force_inflate*/);
}

void DeferredClientWrapper::OnDownloadFailed(
    const std::string& guid,
    const download::CompletionInfo& info,
    FailureReason reason) {
  base::OnceClosure callback =
      base::BindOnce(&DeferredClientWrapper::ForwardOnDownloadFailed,
                     weak_ptr_factory_.GetWeakPtr(), guid, info, reason);
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(true /*force_inflate*/);
}

void DeferredClientWrapper::OnDownloadSucceeded(
    const std::string& guid,
    const CompletionInfo& completion_info) {
  base::OnceClosure callback =
      base::BindOnce(&DeferredClientWrapper::ForwardOnDownloadSucceeded,
                     weak_ptr_factory_.GetWeakPtr(), guid, completion_info);
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(true /*force_inflate*/);
}

bool DeferredClientWrapper::CanServiceRemoveDownloadedFile(
    const std::string& guid,
    bool force_delete) {
  base::OnceClosure callback = base::BindOnce(
      &DeferredClientWrapper::ForwardCanServiceRemoveDownloadedFile,
      weak_ptr_factory_.GetWeakPtr(), guid, force_delete);
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(force_delete /*force_inflate*/);
  return false;
}

void DeferredClientWrapper::GetUploadData(
    const std::string& guid,
    GetUploadDataCallback upload_callback) {
  base::OnceClosure callback = base::BindOnce(
      &DeferredClientWrapper::ForwardGetUploadData,
      weak_ptr_factory_.GetWeakPtr(), guid, std::move(upload_callback));
  deferred_closures_.push_back(std::move(callback));
  RunDeferredClosures(true /*force_inflate*/);
}

void DeferredClientWrapper::ForwardOnServiceInitialized(
    bool state_lost,
    const std::vector<DownloadMetaData>& downloads) {
  wrapped_client_->OnServiceInitialized(state_lost, downloads);
}

void DeferredClientWrapper::ForwardOnServiceUnavailable() {
  wrapped_client_->OnServiceUnavailable();
}

void DeferredClientWrapper::ForwardOnDownloadStarted(
    const std::string& guid,
    const std::vector<GURL>& url_chain,
    const scoped_refptr<const net::HttpResponseHeaders>& headers) {
  wrapped_client_->OnDownloadStarted(guid, url_chain, headers);
}

void DeferredClientWrapper::ForwardOnDownloadUpdated(
    const std::string& guid,
    uint64_t bytes_uploaded,
    uint64_t bytes_downloaded) {
  wrapped_client_->OnDownloadUpdated(guid, bytes_uploaded, bytes_downloaded);
}

void DeferredClientWrapper::ForwardOnDownloadFailed(
    const std::string& guid,
    const download::CompletionInfo& info,
    FailureReason reason) {
  wrapped_client_->OnDownloadFailed(guid, info, reason);
}

void DeferredClientWrapper::ForwardOnDownloadSucceeded(
    const std::string& guid,
    const CompletionInfo& completion_info) {
  wrapped_client_->OnDownloadSucceeded(guid, completion_info);
}

void DeferredClientWrapper::ForwardCanServiceRemoveDownloadedFile(
    const std::string& guid,
    bool force_delete) {
  wrapped_client_->CanServiceRemoveDownloadedFile(guid, force_delete);
}

void DeferredClientWrapper::ForwardGetUploadData(
    const std::string& guid,
    GetUploadDataCallback callback) {
  wrapped_client_->GetUploadData(guid, std::move(callback));
}

void DeferredClientWrapper::RunDeferredClosures(bool force_inflate) {
  if (wrapped_client_) {
    DoRunDeferredClosures();
  } else if (force_inflate) {
#if BUILDFLAG(IS_ANDROID)
    // The constructor registers InflateClient as a callback with
    // FullBrowserTransitionManager on Profile creation. We just need to trigger
    // loading full browser. Once full browser is loaded and  profile is
    // created, FullBrowserTransitionManager will call InflateClient.
    LaunchFullBrowser();
#else
    // For platforms that do not implement reduced mode (i.e. non-android), the
    // wrapped client should have been inflated in the constructor.
    NOTREACHED();
#endif
  }
}

void DeferredClientWrapper::DoRunDeferredClosures() {
  DCHECK(wrapped_client_);
  auto deferred_closures = std::move(deferred_closures_);
  for (auto& closure : deferred_closures) {
    std::move(closure).Run();
  }
}

void DeferredClientWrapper::InflateClient(Profile* profile) {
  DCHECK(profile);
  DCHECK(client_factory_);
  wrapped_client_ = std::move(client_factory_).Run(profile);
  DoRunDeferredClosures();
}

#if BUILDFLAG(IS_ANDROID)
void DeferredClientWrapper::LaunchFullBrowser() {
  if (full_browser_requested_)
    return;
  full_browser_requested_ = true;
  android_startup::LoadFullBrowser();
}
#endif

}  // namespace download
