text: // Copyright 2021 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 "ash/public/cpp/holding_space/holding_space_progress.h"
  
  #include <limits>
  
  #include "base/check_op.h"
  
  namespace ash {
  namespace {
  
  // Helpers ---------------------------------------------------------------------
  
  // Returns whether or not the specified byte counts indicate completion.
  bool CalculateComplete(const absl::optional<int64_t>& current_bytes,
                         const absl::optional<int64_t>& total_bytes) {
    return current_bytes.has_value() && current_bytes == total_bytes;
  }
  
  }  // namespace
  
  // HoldingSpaceProgress --------------------------------------------------------
  
  HoldingSpaceProgress::HoldingSpaceProgress()
      : HoldingSpaceProgress(/*current_bytes=*/0,
                             /*total_bytes=*/0) {}
  
  HoldingSpaceProgress::HoldingSpaceProgress(
      const absl::optional<int64_t>& current_bytes,
      const absl::optional<int64_t>& total_bytes)
      : HoldingSpaceProgress(current_bytes,
                             total_bytes,
                             /*complete=*/absl::nullopt) {}
  
  HoldingSpaceProgress::HoldingSpaceProgress(
      const absl::optional<int64_t>& current_bytes,
      const absl::optional<int64_t>& total_bytes,
      const absl::optional<bool>& complete)
      : HoldingSpaceProgress(current_bytes,
                             total_bytes,
                             complete,
                             /*hidden=*/false) {}
  
  HoldingSpaceProgress::HoldingSpaceProgress(
      const absl::optional<int64_t>& current_bytes,
      const absl::optional<int64_t>& total_bytes,
      const absl::optional<bool>& complete,
      bool hidden)
      : current_bytes_(current_bytes),
        total_bytes_(total_bytes),
        complete_(
            complete.value_or(CalculateComplete(current_bytes_, total_bytes_))),
        hidden_(hidden) {
    DCHECK_GE(current_bytes_.value_or(0), 0);
    DCHECK_GE(total_bytes_.value_or(0), 0);
  
    if (!current_bytes_.has_value() || !total_bytes_.has_value()) {
      DCHECK(!complete_);
      return;
    }
  
    if (complete_) {
      DCHECK_EQ(current_bytes_.value(), total_bytes_.value());
      return;
    }
  
    DCHECK_LE(current_bytes_.value(), total_bytes_.value());
  }
  
  HoldingSpaceProgress::HoldingSpaceProgress(const HoldingSpaceProgress&) =
      default;
  
  HoldingSpaceProgress& HoldingSpaceProgress::operator=(
      const HoldingSpaceProgress&) = default;
  
  HoldingSpaceProgress::~HoldingSpaceProgress() = default;
  
  bool HoldingSpaceProgress::operator==(const HoldingSpaceProgress& rhs) const {
    return std::tie(current_bytes_, total_bytes_, complete_, hidden_) ==
           std::tie(rhs.current_bytes_, rhs.total_bytes_, rhs.complete_,
                    rhs.hidden_);
  }
  
  HoldingSpaceProgress& HoldingSpaceProgress::operator+=(
      const HoldingSpaceProgress& rhs) {
    // Hidden instances shouldn't be included in cumulative progress calculations.
    DCHECK(!hidden_);
    DCHECK(!rhs.hidden_);
  
    HoldingSpaceProgress temp(*this);
    temp = temp + rhs;
  
    current_bytes_ = temp.current_bytes_;
    total_bytes_ = temp.total_bytes_;
    complete_ = temp.complete_;
  
    return *this;
  }
  
  HoldingSpaceProgress HoldingSpaceProgress::operator+(
      const HoldingSpaceProgress& rhs) const {
    // Hidden instances shouldn't be included in cumulative progress calculations.
    DCHECK(!hidden_);
    DCHECK(!rhs.hidden_);
  
    // The number of `current_bytes` should only be present if present for both
    // the lhs and `rhs` instances. Otherwise `current_bytes` is indeterminate.
    absl::optional<int64_t> current_bytes(current_bytes_);
    if (current_bytes.has_value()) {
      current_bytes = rhs.current_bytes_.has_value()
                          ? absl::make_optional(current_bytes.value() +
                                                rhs.current_bytes_.value())
                          : absl::nullopt;
    }
  
    // The number of `total_bytes` should only be present if present for both the
    // lhs and `rhs` instances. Otherwise `total_bytes` is indeterminate.
    absl::optional<int64_t> total_bytes(total_bytes_);
    if (total_bytes.has_value()) {
      total_bytes = rhs.total_bytes_.has_value()
                        ? absl::make_optional(total_bytes.value() +
                                              rhs.total_bytes_.value())
                        : absl::nullopt;
    }
  
    // The result of summing lhs and `rhs` instances is `complete` if and only if
    // both the lhs and `rhs` are themselves complete.
    const bool complete = complete_ && rhs.complete_;
  
    return HoldingSpaceProgress(current_bytes, total_bytes, complete);
  }
  
  absl::optional<float> HoldingSpaceProgress::GetValue() const {
    if (IsComplete())
      return 1.f;
  
    if (IsIndeterminate())
      return absl::nullopt;
  
    // If `current_bytes_` == `total_bytes_` but progress is not complete,
    // return a value that is extremely close but not equal to `1.f`.
    if (current_bytes_.value() == total_bytes_.value())
      return 1.f - std::numeric_limits<float>::epsilon();
  
    return static_cast<double>(current_bytes_.value()) /
           static_cast<double>(total_bytes_.value());
  }
  
  bool HoldingSpaceProgress::IsComplete() const {
    return complete_;
  }
  
  bool HoldingSpaceProgress::IsIndeterminate() const {
    return !current_bytes_.has_value() || !total_bytes_.has_value();
  }
  
  bool HoldingSpaceProgress::IsHidden() const {
    return hidden_;
  }
  
  }  // namespace ash
  