/*
 *  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 "modules/audio_coding/neteq/decision_logic.h"

#include <stdio.h>

#include <string>

#include "absl/types/optional.h"
#include "modules/audio_coding/neteq/packet_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "system_wrappers/include/field_trial.h"

namespace {

constexpr int kPostponeDecodingLevel = 50;
constexpr int kDefaultTargetLevelWindowMs = 100;
constexpr int kDecelerationTargetLevelOffsetMs = 85;

}  // namespace

namespace webrtc {

DecisionLogic::DecisionLogic(NetEqController::Config config)
    : DecisionLogic(config,
                    DelayManager::Create(config.max_packets_in_buffer,
                                         config.base_min_delay_ms,
                                         config.tick_timer),
                    std::make_unique<BufferLevelFilter>()) {}

DecisionLogic::DecisionLogic(
    NetEqController::Config config,
    std::unique_ptr<DelayManager> delay_manager,
    std::unique_ptr<BufferLevelFilter> buffer_level_filter)
    : delay_manager_(std::move(delay_manager)),
      buffer_level_filter_(std::move(buffer_level_filter)),
      tick_timer_(config.tick_timer),
      disallow_time_stretching_(!config.allow_time_stretching),
      timescale_countdown_(
          tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1)),
      estimate_dtx_delay_("estimate_dtx_delay", true),
      time_stretch_cn_("time_stretch_cn", true),
      target_level_window_ms_("target_level_window",
                              kDefaultTargetLevelWindowMs,
                              0,
                              absl::nullopt) {
  const std::string field_trial_name =
      field_trial::FindFullName("WebRTC-Audio-NetEqDecisionLogicSettings");
  ParseFieldTrial(
      {&estimate_dtx_delay_, &time_stretch_cn_, &target_level_window_ms_},
      field_trial_name);
  RTC_LOG(LS_INFO) << "NetEq decision logic settings:"
                      " estimate_dtx_delay="
                   << estimate_dtx_delay_
                   << " time_stretch_cn=" << time_stretch_cn_
                   << " target_level_window_ms=" << target_level_window_ms_;
}

DecisionLogic::~DecisionLogic() = default;

void DecisionLogic::Reset() {
  cng_state_ = kCngOff;
  noise_fast_forward_ = 0;
  packet_length_samples_ = 0;
  sample_memory_ = 0;
  prev_time_scale_ = false;
  last_pack_cng_or_dtmf_ = true;
  timescale_countdown_.reset();
  num_consecutive_expands_ = 0;
  time_stretched_cn_samples_ = 0;
}

void DecisionLogic::SoftReset() {
  packet_length_samples_ = 0;
  sample_memory_ = 0;
  prev_time_scale_ = false;
  last_pack_cng_or_dtmf_ = true;
  timescale_countdown_ =
      tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1);
  time_stretched_cn_samples_ = 0;
  delay_manager_->Reset();
  buffer_level_filter_->Reset();
}

void DecisionLogic::SetSampleRate(int fs_hz, size_t output_size_samples) {
  // TODO(hlundin): Change to an enumerator and skip assert.
  RTC_DCHECK(fs_hz == 8000 || fs_hz == 16000 || fs_hz == 32000 ||
             fs_hz == 48000);
  sample_rate_ = fs_hz;
  output_size_samples_ = output_size_samples;
}

NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status,
                                            bool* reset_decoder) {
  // If last mode was CNG (or Expand, since this could be covering up for
  // a lost CNG packet), remember that CNG is on. This is needed if comfort
  // noise is interrupted by DTMF.
  if (status.last_mode == NetEq::Mode::kRfc3389Cng) {
    cng_state_ = kCngRfc3389On;
  } else if (status.last_mode == NetEq::Mode::kCodecInternalCng) {
    cng_state_ = kCngInternalOn;
  }

  size_t cur_size_samples = estimate_dtx_delay_
                                ? status.packet_buffer_info.span_samples
                                : status.packet_buffer_info.num_samples;
  prev_time_scale_ =
      prev_time_scale_ &&
      (status.last_mode == NetEq::Mode::kAccelerateSuccess ||
       status.last_mode == NetEq::Mode::kAccelerateLowEnergy ||
       status.last_mode == NetEq::Mode::kPreemptiveExpandSuccess ||
       status.last_mode == NetEq::Mode::kPreemptiveExpandLowEnergy);

  // Do not update buffer history if currently playing CNG since it will bias
  // the filtered buffer level.
  if (status.last_mode != NetEq::Mode::kRfc3389Cng &&
      status.last_mode != NetEq::Mode::kCodecInternalCng &&
      !(status.next_packet && status.next_packet->is_dtx &&
        !estimate_dtx_delay_)) {
    FilterBufferLevel(cur_size_samples);
  }

  // Guard for errors, to avoid getting stuck in error mode.
  if (status.last_mode == NetEq::Mode::kError) {
    if (!status.next_packet) {
      return NetEq::Operation::kExpand;
    } else {
      // Use kUndefined to flag for a reset.
      return NetEq::Operation::kUndefined;
    }
  }

  if (status.next_packet && status.next_packet->is_cng) {
    return CngOperation(status.last_mode, status.target_timestamp,
                        status.next_packet->timestamp,
                        status.generated_noise_samples);
  }

  // Handle the case with no packet at all available (except maybe DTMF).
  if (!status.next_packet) {
    return NoPacket(status.play_dtmf);
  }

  // If the expand period was very long, reset NetEQ since it is likely that the
  // sender was restarted.
  if (num_consecutive_expands_ > kReinitAfterExpands) {
    *reset_decoder = true;
    return NetEq::Operation::kNormal;
  }

  // Make sure we don't restart audio too soon after an expansion to avoid
  // running out of data right away again. We should only wait if there are no
  // DTX or CNG packets in the buffer (otherwise we should just play out what we
  // have, since we cannot know the exact duration of DTX or CNG packets), and
  // if the mute factor is low enough (otherwise the expansion was short enough
  // to not be noticable).
  // Note that the MuteFactor is in Q14, so a value of 16384 corresponds to 1.
  const size_t current_span =
      estimate_dtx_delay_ ? status.packet_buffer_info.span_samples
                          : status.packet_buffer_info.span_samples_no_dtx;
  const int target_level_samples =
      delay_manager_->TargetDelayMs() * sample_rate_ / 1000;
  if ((status.last_mode == NetEq::Mode::kExpand ||
       status.last_mode == NetEq::Mode::kCodecPlc) &&
      status.expand_mutefactor < 16384 / 2 &&
      current_span < static_cast<size_t>(target_level_samples *
                                         kPostponeDecodingLevel / 100) &&
      !status.packet_buffer_info.dtx_or_cng) {
    return NetEq::Operation::kExpand;
  }

  const uint32_t five_seconds_samples = static_cast<uint32_t>(5 * sample_rate_);
  // Check if the required packet is available.
  if (status.target_timestamp == status.next_packet->timestamp) {
    return ExpectedPacketAvailable(status.last_mode, status.play_dtmf);
  } else if (!PacketBuffer::IsObsoleteTimestamp(status.next_packet->timestamp,
                                                status.target_timestamp,
                                                five_seconds_samples)) {
    return FuturePacketAvailable(
        status.last_packet_samples, status.last_mode, status.target_timestamp,
        status.next_packet->timestamp, status.play_dtmf,
        status.generated_noise_samples, status.packet_buffer_info.span_samples,
        status.packet_buffer_info.num_packets);
  } else {
    // This implies that available_timestamp < target_timestamp, which can
    // happen when a new stream or codec is received. Signal for a reset.
    return NetEq::Operation::kUndefined;
  }
}

void DecisionLogic::ExpandDecision(NetEq::Operation operation) {
  if (operation == NetEq::Operation::kExpand) {
    num_consecutive_expands_++;
  } else {
    num_consecutive_expands_ = 0;
  }
}

absl::optional<int> DecisionLogic::PacketArrived(
    int fs_hz,
    bool should_update_stats,
    const PacketArrivedInfo& info) {
  buffer_flush_ = buffer_flush_ || info.buffer_flush;
  if (info.is_cng_or_dtmf) {
    last_pack_cng_or_dtmf_ = true;
    return absl::nullopt;
  }
  if (!should_update_stats) {
    return absl::nullopt;
  }
  if (info.packet_length_samples > 0 && fs_hz > 0 &&
      info.packet_length_samples != packet_length_samples_) {
    packet_length_samples_ = info.packet_length_samples;
    delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz);
  }
  auto relative_delay = delay_manager_->Update(
      info.main_timestamp, fs_hz, /*reset=*/last_pack_cng_or_dtmf_);
  last_pack_cng_or_dtmf_ = false;
  return relative_delay;
}

void DecisionLogic::FilterBufferLevel(size_t buffer_size_samples) {
  buffer_level_filter_->SetTargetBufferLevel(delay_manager_->TargetDelayMs());

  int time_stretched_samples = time_stretched_cn_samples_;
  if (prev_time_scale_) {
    time_stretched_samples += sample_memory_;
    timescale_countdown_ = tick_timer_->GetNewCountdown(kMinTimescaleInterval);
  }

  if (buffer_flush_) {
    buffer_level_filter_->SetFilteredBufferLevel(buffer_size_samples);
    buffer_flush_ = false;
  } else {
    buffer_level_filter_->Update(buffer_size_samples, time_stretched_samples);
  }
  prev_time_scale_ = false;
  time_stretched_cn_samples_ = 0;
}

NetEq::Operation DecisionLogic::CngOperation(NetEq::Mode prev_mode,
                                             uint32_t target_timestamp,
                                             uint32_t available_timestamp,
                                             size_t generated_noise_samples) {
  // Signed difference between target and available timestamp.
  int32_t timestamp_diff = static_cast<int32_t>(
      static_cast<uint32_t>(generated_noise_samples + target_timestamp) -
      available_timestamp);
  int optimal_level_samp =
      delay_manager_->TargetDelayMs() * sample_rate_ / 1000;
  const int64_t excess_waiting_time_samp =
      -static_cast<int64_t>(timestamp_diff) - optimal_level_samp;

  if (excess_waiting_time_samp > optimal_level_samp / 2) {
    // The waiting time for this packet will be longer than 1.5
    // times the wanted buffer delay. Apply fast-forward to cut the
    // waiting time down to the optimal.
    noise_fast_forward_ = rtc::saturated_cast<size_t>(noise_fast_forward_ +
                                                      excess_waiting_time_samp);
    timestamp_diff =
        rtc::saturated_cast<int32_t>(timestamp_diff + excess_waiting_time_samp);
  }

  if (timestamp_diff < 0 && prev_mode == NetEq::Mode::kRfc3389Cng) {
    // Not time to play this packet yet. Wait another round before using this
    // packet. Keep on playing CNG from previous CNG parameters.
    return NetEq::Operation::kRfc3389CngNoPacket;
  } else {
    // Otherwise, go for the CNG packet now.
    noise_fast_forward_ = 0;
    return NetEq::Operation::kRfc3389Cng;
  }
}

NetEq::Operation DecisionLogic::NoPacket(bool play_dtmf) {
  if (cng_state_ == kCngRfc3389On) {
    // Keep on playing comfort noise.
    return NetEq::Operation::kRfc3389CngNoPacket;
  } else if (cng_state_ == kCngInternalOn) {
    // Keep on playing codec internal comfort noise.
    return NetEq::Operation::kCodecInternalCng;
  } else if (play_dtmf) {
    return NetEq::Operation::kDtmf;
  } else {
    // Nothing to play, do expand.
    return NetEq::Operation::kExpand;
  }
}

NetEq::Operation DecisionLogic::ExpectedPacketAvailable(NetEq::Mode prev_mode,
                                                        bool play_dtmf) {
  if (!disallow_time_stretching_ && prev_mode != NetEq::Mode::kExpand &&
      !play_dtmf) {
    const int samples_per_ms = sample_rate_ / 1000;
    const int target_level_samples =
        delay_manager_->TargetDelayMs() * samples_per_ms;
    const int low_limit =
        std::max(target_level_samples * 3 / 4,
                 target_level_samples -
                     kDecelerationTargetLevelOffsetMs * samples_per_ms);
    // `higher_limit` is equal to `target_level`, but should at
    // least be 20 ms higher than `lower_limit`.
    const int high_limit =
        std::max(target_level_samples, low_limit + 20 * samples_per_ms);

    const int buffer_level_samples =
        buffer_level_filter_->filtered_current_level();
    if (buffer_level_samples >= high_limit << 2)
      return NetEq::Operation::kFastAccelerate;
    if (TimescaleAllowed()) {
      if (buffer_level_samples >= high_limit)
        return NetEq::Operation::kAccelerate;
      if (buffer_level_samples < low_limit)
        return NetEq::Operation::kPreemptiveExpand;
    }
  }
  return NetEq::Operation::kNormal;
}

NetEq::Operation DecisionLogic::FuturePacketAvailable(
    size_t decoder_frame_length,
    NetEq::Mode prev_mode,
    uint32_t target_timestamp,
    uint32_t available_timestamp,
    bool play_dtmf,
    size_t generated_noise_samples,
    size_t span_samples_in_packet_buffer,
    size_t num_packets_in_packet_buffer) {
  // Required packet is not available, but a future packet is.
  // Check if we should continue with an ongoing expand because the new packet
  // is too far into the future.
  uint32_t timestamp_leap = available_timestamp - target_timestamp;
  if ((prev_mode == NetEq::Mode::kExpand ||
       prev_mode == NetEq::Mode::kCodecPlc) &&
      !ReinitAfterExpands(timestamp_leap) && !MaxWaitForPacket() &&
      PacketTooEarly(timestamp_leap) && UnderTargetLevel()) {
    if (play_dtmf) {
      // Still have DTMF to play, so do not do expand.
      return NetEq::Operation::kDtmf;
    } else {
      // Nothing to play.
      return NetEq::Operation::kExpand;
    }
  }

  if (prev_mode == NetEq::Mode::kCodecPlc) {
    return NetEq::Operation::kNormal;
  }

  // If previous was comfort noise, then no merge is needed.
  if (prev_mode == NetEq::Mode::kRfc3389Cng ||
      prev_mode == NetEq::Mode::kCodecInternalCng) {
    size_t cur_size_samples =
        estimate_dtx_delay_
            ? span_samples_in_packet_buffer
            : num_packets_in_packet_buffer * decoder_frame_length;
    // Target level is in number of packets in Q8.
    const size_t target_level_samples =
        delay_manager_->TargetDelayMs() * sample_rate_ / 1000;
    const bool generated_enough_noise =
        static_cast<uint32_t>(generated_noise_samples + target_timestamp) >=
        available_timestamp;

    if (time_stretch_cn_) {
      const size_t target_threshold_samples =
          target_level_window_ms_ / 2 * (sample_rate_ / 1000);
      const bool above_target_window =
          cur_size_samples > target_level_samples + target_threshold_samples;
      const bool below_target_window =
          target_level_samples > target_threshold_samples &&
          cur_size_samples < target_level_samples - target_threshold_samples;
      // Keep the delay same as before CNG, but make sure that it is within the
      // target window.
      if ((generated_enough_noise && !below_target_window) ||
          above_target_window) {
        time_stretched_cn_samples_ = timestamp_leap - generated_noise_samples;
        return NetEq::Operation::kNormal;
      }
    } else {
      // Keep the same delay as before the CNG, but make sure that the number of
      // samples in buffer is no higher than 4 times the optimal level.
      if (generated_enough_noise ||
          cur_size_samples > target_level_samples * 4) {
        // Time to play this new packet.
        return NetEq::Operation::kNormal;
      }
    }

    // Too early to play this new packet; keep on playing comfort noise.
    if (prev_mode == NetEq::Mode::kRfc3389Cng) {
      return NetEq::Operation::kRfc3389CngNoPacket;
    }
    // prevPlayMode == kModeCodecInternalCng.
    return NetEq::Operation::kCodecInternalCng;
  }

  // Do not merge unless we have done an expand before.
  if (prev_mode == NetEq::Mode::kExpand) {
    return NetEq::Operation::kMerge;
  } else if (play_dtmf) {
    // Play DTMF instead of expand.
    return NetEq::Operation::kDtmf;
  } else {
    return NetEq::Operation::kExpand;
  }
}

bool DecisionLogic::UnderTargetLevel() const {
  return buffer_level_filter_->filtered_current_level() <
         delay_manager_->TargetDelayMs() * sample_rate_ / 1000;
}

bool DecisionLogic::ReinitAfterExpands(uint32_t timestamp_leap) const {
  return timestamp_leap >=
         static_cast<uint32_t>(output_size_samples_ * kReinitAfterExpands);
}

bool DecisionLogic::PacketTooEarly(uint32_t timestamp_leap) const {
  return timestamp_leap >
         static_cast<uint32_t>(output_size_samples_ * num_consecutive_expands_);
}

bool DecisionLogic::MaxWaitForPacket() const {
  return num_consecutive_expands_ >= kMaxWaitForPacket;
}

}  // namespace webrtc
