// Copyright 2015 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/task_manager/providers/task.h"

#include <stddef.h>

#include <optional>

#include "base/numerics/safe_conversions.h"
#include "base/process/process.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/task_manager/providers/task_provider_observer.h"
#include "chrome/browser/task_manager/task_manager_observer.h"
#include "content/public/common/result_codes.h"
#include "ui/base/resource/resource_bundle.h"

namespace task_manager {

namespace {

// The last ID given to the previously created task.
int64_t g_last_id = 0;

base::ProcessId DetermineProcessId(base::ProcessHandle handle,
                                   base::ProcessId process_id) {
  if (process_id != base::kNullProcessId)
    return process_id;
  return base::GetProcId(handle);
}

}  // namespace

Task::Task(const std::u16string& title,
           const gfx::ImageSkia* icon,
           base::ProcessHandle handle,
           base::ProcessId process_id)
    : task_id_(g_last_id++),
      title_(title),
      icon_(icon ? *icon : gfx::ImageSkia()),
      process_handle_(handle),
      process_id_(DetermineProcessId(handle, process_id)) {}

Task::~Task() = default;

// static
std::u16string Task::GetProfileNameFromProfile(Profile* profile) {
  DCHECK(profile);
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(
              profile->GetOriginalProfile()->GetPath());
  return entry ? entry->GetName() : std::u16string();
}

void Task::Activate() {}

bool Task::IsKillable() {
  // Protects from trying to kill a task that doesn't have an accurate process
  // Id yet. This can result in calling "kill 0" which kills all processes in
  // the process group.
  if (process_id() == base::kNullProcessId)
    return false;
  return true;
}

bool Task::Kill() {
  if (!IsKillable())
    return false;
  DCHECK_NE(process_id(), base::GetCurrentProcId());
  base::Process process = base::Process::Open(process_id());
  return process.Terminate(content::RESULT_CODE_KILLED, false);
}

void Task::Refresh(const base::TimeDelta& update_interval,
                   int64_t refresh_flags) {
  if ((refresh_flags & REFRESH_TYPE_NETWORK_USAGE) == 0 ||
      update_interval == base::TimeDelta())
    return;

  base::ByteSizeDelta current_cycle_read_byte_count =
      cumulative_bytes_read_ - last_refresh_cumulative_bytes_read_;
  network_read_rate_ = base::ByteSize(base::ClampRound<uint64_t>(
      current_cycle_read_byte_count.InBytesF() / update_interval.InSecondsF()));

  base::ByteSizeDelta current_cycle_sent_byte_count =
      cumulative_bytes_sent_ - last_refresh_cumulative_bytes_sent_;
  network_sent_rate_ = base::ByteSize(base::ClampRound<uint64_t>(
      current_cycle_sent_byte_count.InBytesF() / update_interval.InSecondsF()));

  last_refresh_cumulative_bytes_read_ = cumulative_bytes_read_;
  last_refresh_cumulative_bytes_sent_ = cumulative_bytes_sent_;
}

void Task::UpdateProcessInfo(base::ProcessHandle handle,
                             base::ProcessId process_id,
                             TaskProviderObserver* observer) {
  process_id = DetermineProcessId(handle, process_id);

  // Don't remove the task if there is no change to the process ID.
  if (process_id == process_id_)
    return;

  // TaskManagerImpl and TaskGroup implementations assume that a process ID is
  // consistent for the lifetime of a Task. So to change the process ID,
  // temporarily unregister this Task.
  observer->TaskRemoved(this);
  process_handle_ = handle;
  process_id_ = process_id;
  observer->TaskAdded(this);
}

void Task::OnNetworkBytesRead(base::ByteSize bytes_read) {
  cumulative_bytes_read_ += bytes_read;
}

void Task::OnNetworkBytesSent(base::ByteSize bytes_sent) {
  cumulative_bytes_sent_ += bytes_sent;
}

Task::SubType Task::GetSubType() const {
  return Task::SubType::kNoSubType;
}

void Task::GetTerminationStatus(base::TerminationStatus* out_status,
                                int* out_error_code) const {
  DCHECK(out_status);
  DCHECK(out_error_code);

  *out_status = base::TERMINATION_STATUS_STILL_RUNNING;
  *out_error_code = 0;
}

std::u16string Task::GetProfileName() const {
  return std::u16string();
}

SessionID Task::GetTabId() const {
  return SessionID::InvalidValue();
}

bool Task::HasParentTask() const {
  return GetParentTask() != nullptr;
}

base::WeakPtr<Task> Task::GetParentTask() const {
  return nullptr;
}

bool Task::ReportsSqliteMemory() const {
  return GetSqliteMemoryUsed().has_value();
}

std::optional<base::ByteSize> Task::GetSqliteMemoryUsed() const {
  return std::nullopt;
}

std::optional<base::ByteSize> Task::GetV8MemoryAllocated() const {
  return std::nullopt;
}

std::optional<base::ByteSize> Task::GetV8MemoryUsed() const {
  return std::nullopt;
}

bool Task::ReportsWebCacheStats() const {
  return false;
}

blink::WebCacheResourceTypeStats Task::GetWebCacheStats() const {
  return blink::WebCacheResourceTypeStats();
}

int Task::GetKeepaliveCount() const {
  return -1;
}

bool Task::IsRunningInVM() const {
  return false;
}

base::ByteSize Task::GetNetworkUsageRate() const {
  return network_sent_rate_ + network_read_rate_;
}

base::ByteSize Task::GetCumulativeNetworkUsage() const {
  return cumulative_bytes_sent_ + cumulative_bytes_read_;
}

// static
gfx::ImageSkia* Task::FetchIcon(int id, gfx::ImageSkia** result_image) {
  if (!*result_image && ui::ResourceBundle::HasSharedInstance()) {
    *result_image =
        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id);
    if (*result_image)
      (*result_image)->MakeThreadSafe();
  }
  return *result_image;
}

base::WeakPtr<Task> Task::AsWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

}  // namespace task_manager
