/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_coding/neteq/statistics_calculator.h"

#include <assert.h>
#include <string.h>  // memset

#include "webrtc/modules/audio_coding/neteq/decision_logic.h"
#include "webrtc/modules/audio_coding/neteq/delay_manager.h"

namespace webrtc {

StatisticsCalculator::StatisticsCalculator()
    : preemptive_samples_(0),
      accelerate_samples_(0),
      added_zero_samples_(0),
      expanded_speech_samples_(0),
      expanded_noise_samples_(0),
      discarded_packets_(0),
      lost_timestamps_(0),
      timestamps_since_last_report_(0),
      len_waiting_times_(0),
      next_waiting_time_index_(0),
      secondary_decoded_samples_(0) {
  memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0]));
}

void StatisticsCalculator::Reset() {
  preemptive_samples_ = 0;
  accelerate_samples_ = 0;
  added_zero_samples_ = 0;
  expanded_speech_samples_ = 0;
  expanded_noise_samples_ = 0;
  secondary_decoded_samples_ = 0;
}

void StatisticsCalculator::ResetMcu() {
  discarded_packets_ = 0;
  lost_timestamps_ = 0;
  timestamps_since_last_report_ = 0;
}

void StatisticsCalculator::ResetWaitingTimeStatistics() {
  memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0]));
  len_waiting_times_ = 0;
  next_waiting_time_index_ = 0;
}

void StatisticsCalculator::ExpandedVoiceSamples(int num_samples) {
  expanded_speech_samples_ += num_samples;
}

void StatisticsCalculator::ExpandedNoiseSamples(int num_samples) {
  expanded_noise_samples_ += num_samples;
}

void StatisticsCalculator::PreemptiveExpandedSamples(int num_samples) {
  preemptive_samples_ += num_samples;
}

void StatisticsCalculator::AcceleratedSamples(int num_samples) {
  accelerate_samples_ += num_samples;
}

void StatisticsCalculator::AddZeros(int num_samples) {
  added_zero_samples_ += num_samples;
}

void StatisticsCalculator::PacketsDiscarded(int num_packets) {
  discarded_packets_ += num_packets;
}

void StatisticsCalculator::LostSamples(int num_samples) {
  lost_timestamps_ += num_samples;
}

void StatisticsCalculator::IncreaseCounter(int num_samples, int fs_hz) {
  timestamps_since_last_report_ += num_samples;
  if (timestamps_since_last_report_ >
      static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
    lost_timestamps_ = 0;
    timestamps_since_last_report_ = 0;
    discarded_packets_ = 0;
  }
}

void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
  secondary_decoded_samples_ += num_samples;
}

void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
  assert(next_waiting_time_index_ < kLenWaitingTimes);
  waiting_times_[next_waiting_time_index_] = waiting_time_ms;
  next_waiting_time_index_++;
  if (next_waiting_time_index_ >= kLenWaitingTimes) {
    next_waiting_time_index_ = 0;
  }
  if (len_waiting_times_ < kLenWaitingTimes) {
    len_waiting_times_++;
  }
}

void StatisticsCalculator::GetNetworkStatistics(
    int fs_hz,
    int num_samples_in_buffers,
    int samples_per_packet,
    const DelayManager& delay_manager,
    const DecisionLogic& decision_logic,
    NetEqNetworkStatistics *stats) {
  if (fs_hz <= 0 || !stats) {
    assert(false);
    return;
  }

  stats->added_zero_samples = added_zero_samples_;
  stats->current_buffer_size_ms = num_samples_in_buffers * 1000 / fs_hz;
  const int ms_per_packet = decision_logic.packet_length_samples() /
      (fs_hz / 1000);
  stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) *
      ms_per_packet;
  stats->jitter_peaks_found = delay_manager.PeakFound();
  stats->clockdrift_ppm = delay_manager.AverageIAT();

  stats->packet_loss_rate =
      CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_);

  const unsigned discarded_samples = discarded_packets_ * samples_per_packet;
  stats->packet_discard_rate =
      CalculateQ14Ratio(discarded_samples, timestamps_since_last_report_);

  stats->accelerate_rate =
      CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);

  stats->preemptive_rate =
      CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);

  stats->expand_rate =
      CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
                        timestamps_since_last_report_);

  stats->speech_expand_rate =
      CalculateQ14Ratio(expanded_speech_samples_,
      timestamps_since_last_report_);

  stats->secondary_decoded_rate =
      CalculateQ14Ratio(secondary_decoded_samples_,
                        timestamps_since_last_report_);

  // Reset counters.
  ResetMcu();
  Reset();
}

void StatisticsCalculator::WaitingTimes(std::vector<int>* waiting_times) {
  if (!waiting_times) {
    return;
  }
  waiting_times->assign(waiting_times_, waiting_times_ + len_waiting_times_);
  ResetWaitingTimeStatistics();
}

int StatisticsCalculator::CalculateQ14Ratio(uint32_t numerator,
                                            uint32_t denominator) {
  if (numerator == 0) {
    return 0;
  } else if (numerator < denominator) {
    // Ratio must be smaller than 1 in Q14.
    assert((numerator << 14) / denominator < (1 << 14));
    return (numerator << 14) / denominator;
  } else {
    // Will not produce a ratio larger than 1, since this is probably an error.
    return 1 << 14;
  }
}

}  // namespace webrtc
