// Copyright 2014 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 "media/cast/common/clock_drift_smoother.h"

#include <stdint.h>

#include "base/logging.h"

namespace media {
namespace cast {

ClockDriftSmoother::ClockDriftSmoother(base::TimeDelta time_constant)
    : time_constant_(time_constant),
      estimate_us_(0.0) {
  DCHECK(time_constant_ > base::TimeDelta());
}

ClockDriftSmoother::~ClockDriftSmoother() {}

base::TimeDelta ClockDriftSmoother::Current() const {
  DCHECK(!last_update_time_.is_null());
  return base::TimeDelta::FromMicroseconds(static_cast<int64_t>(
      estimate_us_ + 0.5));  // Round to nearest microsecond.
}

void ClockDriftSmoother::Reset(base::TimeTicks now,
                               base::TimeDelta measured_offset) {
  DCHECK(!now.is_null());
  last_update_time_ = now;
  estimate_us_ = static_cast<double>(measured_offset.InMicroseconds());
}

void ClockDriftSmoother::Update(base::TimeTicks now,
                                base::TimeDelta measured_offset) {
  DCHECK(!now.is_null());
  if (last_update_time_.is_null()) {
    Reset(now, measured_offset);
  } else if (now < last_update_time_) {
    // |now| is not monotonically non-decreasing.
    NOTREACHED();
  } else {
    const double elapsed_us =
        static_cast<double>((now - last_update_time_).InMicroseconds());
    last_update_time_ = now;
    const double weight =
        elapsed_us / (elapsed_us + time_constant_.InMicroseconds());
    estimate_us_ = weight * measured_offset.InMicroseconds() +
        (1.0 - weight) * estimate_us_;
  }
}

// static
base::TimeDelta ClockDriftSmoother::GetDefaultTimeConstant() {
  static const int kDefaultTimeConstantInSeconds = 30;
  return base::TimeDelta::FromSeconds(kDefaultTimeConstantInSeconds);
}

}  // namespace cast
}  // namespace media
