// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/web/download/download_controller_impl.h"

#import "base/strings/sys_string_conversions.h"
#import "base/task/sequenced_task_runner.h"
#import "base/task/task_traits.h"
#import "base/task/thread_pool.h"
#import "ios/web/download/download_native_task_bridge.h"
#import "ios/web/download/download_native_task_impl.h"
#import "ios/web/download/web_state_content_download_task.h"
#import "ios/web/public/browser_state.h"
#import "ios/web/public/download/download_controller_delegate.h"
#import "ios/web/public/web_state.h"
#import "net/base/apple/url_conversions.h"
#import "net/http/http_request_headers.h"

namespace {
const char kDownloadControllerKey = 0;
}  // namespace

namespace web {

// static
DownloadController* DownloadController::FromBrowserState(
    BrowserState* browser_state) {
  DCHECK(browser_state);
  if (!browser_state->GetUserData(&kDownloadControllerKey)) {
    browser_state->SetUserData(&kDownloadControllerKey,
                               std::make_unique<DownloadControllerImpl>());
  }
  return static_cast<DownloadControllerImpl*>(
      browser_state->GetUserData(&kDownloadControllerKey));
}

DownloadControllerImpl::DownloadControllerImpl()
    : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::USER_BLOCKING})) {}

DownloadControllerImpl::~DownloadControllerImpl() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  for (DownloadTask* task : alive_tasks_) {
    task->RemoveObserver(this);
    task->Cancel();
  }

  if (delegate_) {
    delegate_->OnDownloadControllerDestroyed(this);
  }

  DCHECK(!delegate_);
}

void DownloadControllerImpl::CreateWebStateDownloadTask(WebState* web_state,
                                                        NSString* identifier,
                                                        int64_t total_bytes) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!delegate_) {
    return;
  }
  OnDownloadCreated(std::make_unique<WebStateContentDownloadTask>(
      web_state, web_state->GetLastCommittedURL(),
      base::SysUTF8ToNSString(web_state->GetLastCommittedURL().GetHost()), @"",
      "", total_bytes, web_state->GetContentsMimeType(), identifier,
      task_runner_));
}

void DownloadControllerImpl::CreateNativeDownloadTask(
    WebState* web_state,
    NSString* identifier,
    const GURL& original_url,
    NSString* originating_host,
    NSString* http_method,
    const std::string& content_disposition,
    int64_t total_bytes,
    const std::string& mime_type,
    DownloadNativeTaskBridge* download) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!delegate_) {
    [download cancel];
    return;
  }

  OnDownloadCreated(std::make_unique<DownloadNativeTaskImpl>(
      web_state, original_url, originating_host, http_method,
      content_disposition, total_bytes, mime_type, identifier, task_runner_,
      download));
}

void DownloadControllerImpl::SetDelegate(DownloadControllerDelegate* delegate) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  delegate_ = delegate;
}

DownloadControllerDelegate* DownloadControllerImpl::GetDelegate() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return delegate_;
}

void DownloadControllerImpl::OnDownloadDestroyed(DownloadTask* task) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto it = alive_tasks_.find(task);
  DCHECK(it != alive_tasks_.end());
  task->RemoveObserver(this);
  alive_tasks_.erase(it);
}

void DownloadControllerImpl::OnDownloadCreated(
    std::unique_ptr<DownloadTaskImpl> task) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(task);
  alive_tasks_.insert(task.get());
  task->AddObserver(this);

  DCHECK(task->GetWebState());
  WebState* web_state = task->GetWebState();
  delegate_->OnDownloadCreated(this, web_state, std::move(task));
}

}  // namespace web
