// 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 "net/third_party/quiche/src/quic/core/quic_clock.h"

#include <limits>

#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"

namespace quic {

QuicClock::QuicClock()
    : is_calibrated_(false), calibration_offset_(QuicTime::Delta::Zero()) {}

QuicClock::~QuicClock() {}

QuicTime::Delta QuicClock::ComputeCalibrationOffset() const {
  // In the ideal world, all we need to do is to return the difference of
  // WallNow() and Now(). In the real world, things like context switch may
  // happen between the calls to WallNow() and Now(), causing their difference
  // to be arbitrarily large, so we repeat the calculation many times and use
  // the one with the minimum difference as the true offset.
  int64_t min_offset_us = std::numeric_limits<int64_t>::max();

  for (int i = 0; i < 128; ++i) {
    int64_t now_in_us = (Now() - QuicTime::Zero()).ToMicroseconds();
    int64_t wallnow_in_us =
        static_cast<int64_t>(WallNow().ToUNIXMicroseconds());

    int64_t offset_us = wallnow_in_us - now_in_us;
    if (offset_us < min_offset_us) {
      min_offset_us = offset_us;
    }
  }

  return QuicTime::Delta::FromMicroseconds(min_offset_us);
}

void QuicClock::SetCalibrationOffset(QuicTime::Delta offset) {
  DCHECK(!is_calibrated_) << "A clock should only be calibrated once";
  calibration_offset_ = offset;
  is_calibrated_ = true;
}

QuicTime QuicClock::ConvertWallTimeToQuicTime(
    const QuicWallTime& walltime) const {
  if (is_calibrated_) {
    int64_t time_in_us = static_cast<int64_t>(walltime.ToUNIXMicroseconds()) -
                         calibration_offset_.ToMicroseconds();
    return QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(time_in_us);
  }

  //     ..........................
  //     |            |           |
  // unix epoch   |walltime|   WallNow()
  //     ..........................
  //            |     |           |
  //     clock epoch  |         Now()
  //               result
  //
  // result = Now() - (WallNow() - walltime)
  return Now() - QuicTime::Delta::FromMicroseconds(
                     WallNow()
                         .Subtract(QuicTime::Delta::FromMicroseconds(
                             walltime.ToUNIXMicroseconds()))
                         .ToUNIXMicroseconds());
}

}  // namespace quic
