/*
 *  Copyright 2018 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 "media/base/fake_media_engine.h"

#include <memory>
#include <utility>

#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "rtc_base/checks.h"

namespace cricket {
using webrtc::TaskQueueBase;

FakeVoiceMediaChannel::DtmfInfo::DtmfInfo(uint32_t ssrc,
                                          int event_code,
                                          int duration)
    : ssrc(ssrc), event_code(event_code), duration(duration) {}

FakeVoiceMediaChannel::VoiceChannelAudioSink::VoiceChannelAudioSink(
    AudioSource* source)
    : source_(source) {
  source_->SetSink(this);
}
FakeVoiceMediaChannel::VoiceChannelAudioSink::~VoiceChannelAudioSink() {
  if (source_) {
    source_->SetSink(nullptr);
  }
}
void FakeVoiceMediaChannel::VoiceChannelAudioSink::OnData(
    const void* audio_data,
    int bits_per_sample,
    int sample_rate,
    size_t number_of_channels,
    size_t number_of_frames,
    absl::optional<int64_t> absolute_capture_timestamp_ms) {}
void FakeVoiceMediaChannel::VoiceChannelAudioSink::OnClose() {
  source_ = nullptr;
}
AudioSource* FakeVoiceMediaChannel::VoiceChannelAudioSink::source() const {
  return source_;
}

FakeVoiceMediaChannel::FakeVoiceMediaChannel(FakeVoiceEngine* engine,
                                             const AudioOptions& options,
                                             TaskQueueBase* network_thread)
    : RtpHelper<VoiceMediaChannel>(network_thread),
      engine_(engine),
      max_bps_(-1) {
  output_scalings_[0] = 1.0;  // For default channel.
  SetOptions(options);
}
FakeVoiceMediaChannel::~FakeVoiceMediaChannel() {
  if (engine_) {
    engine_->UnregisterChannel(this);
  }
}
const std::vector<AudioCodec>& FakeVoiceMediaChannel::recv_codecs() const {
  return recv_codecs_;
}
const std::vector<AudioCodec>& FakeVoiceMediaChannel::send_codecs() const {
  return send_codecs_;
}
const std::vector<AudioCodec>& FakeVoiceMediaChannel::codecs() const {
  return send_codecs();
}
const std::vector<FakeVoiceMediaChannel::DtmfInfo>&
FakeVoiceMediaChannel::dtmf_info_queue() const {
  return dtmf_info_queue_;
}
const AudioOptions& FakeVoiceMediaChannel::options() const {
  return options_;
}
int FakeVoiceMediaChannel::max_bps() const {
  return max_bps_;
}
bool FakeVoiceMediaChannel::SetSendParameters(
    const AudioSendParameters& params) {
  set_send_rtcp_parameters(params.rtcp);
  return (SetSendCodecs(params.codecs) &&
          SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
          SetSendRtpHeaderExtensions(params.extensions) &&
          SetMaxSendBandwidth(params.max_bandwidth_bps) &&
          SetOptions(params.options));
}
bool FakeVoiceMediaChannel::SetRecvParameters(
    const AudioRecvParameters& params) {
  set_recv_rtcp_parameters(params.rtcp);
  return (SetRecvCodecs(params.codecs) &&
          SetRecvRtpHeaderExtensions(params.extensions));
}
void FakeVoiceMediaChannel::SetPlayout(bool playout) {
  set_playout(playout);
}
void FakeVoiceMediaChannel::SetSend(bool send) {
  set_sending(send);
}
bool FakeVoiceMediaChannel::SetAudioSend(uint32_t ssrc,
                                         bool enable,
                                         const AudioOptions* options,
                                         AudioSource* source) {
  if (!SetLocalSource(ssrc, source)) {
    return false;
  }
  if (!RtpHelper<VoiceMediaChannel>::MuteStream(ssrc, !enable)) {
    return false;
  }
  if (enable && options) {
    return SetOptions(*options);
  }
  return true;
}
bool FakeVoiceMediaChannel::HasSource(uint32_t ssrc) const {
  return local_sinks_.find(ssrc) != local_sinks_.end();
}
bool FakeVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
  if (!RtpHelper<VoiceMediaChannel>::AddRecvStream(sp))
    return false;
  output_scalings_[sp.first_ssrc()] = 1.0;
  output_delays_[sp.first_ssrc()] = 0;
  return true;
}
bool FakeVoiceMediaChannel::RemoveRecvStream(uint32_t ssrc) {
  if (!RtpHelper<VoiceMediaChannel>::RemoveRecvStream(ssrc))
    return false;
  output_scalings_.erase(ssrc);
  output_delays_.erase(ssrc);
  return true;
}
bool FakeVoiceMediaChannel::CanInsertDtmf() {
  for (std::vector<AudioCodec>::const_iterator it = send_codecs_.begin();
       it != send_codecs_.end(); ++it) {
    // Find the DTMF telephone event "codec".
    if (absl::EqualsIgnoreCase(it->name, "telephone-event")) {
      return true;
    }
  }
  return false;
}
bool FakeVoiceMediaChannel::InsertDtmf(uint32_t ssrc,
                                       int event_code,
                                       int duration) {
  dtmf_info_queue_.push_back(DtmfInfo(ssrc, event_code, duration));
  return true;
}
bool FakeVoiceMediaChannel::SetOutputVolume(uint32_t ssrc, double volume) {
  if (output_scalings_.find(ssrc) != output_scalings_.end()) {
    output_scalings_[ssrc] = volume;
    return true;
  }
  return false;
}
bool FakeVoiceMediaChannel::SetDefaultOutputVolume(double volume) {
  for (auto& entry : output_scalings_) {
    entry.second = volume;
  }
  return true;
}
bool FakeVoiceMediaChannel::GetOutputVolume(uint32_t ssrc, double* volume) {
  if (output_scalings_.find(ssrc) == output_scalings_.end())
    return false;
  *volume = output_scalings_[ssrc];
  return true;
}
bool FakeVoiceMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
                                                         int delay_ms) {
  if (output_delays_.find(ssrc) == output_delays_.end()) {
    return false;
  } else {
    output_delays_[ssrc] = delay_ms;
    return true;
  }
}
absl::optional<int> FakeVoiceMediaChannel::GetBaseMinimumPlayoutDelayMs(
    uint32_t ssrc) const {
  const auto it = output_delays_.find(ssrc);
  if (it != output_delays_.end()) {
    return it->second;
  }
  return absl::nullopt;
}
bool FakeVoiceMediaChannel::GetStats(VoiceMediaInfo* info,
                                     bool get_and_clear_legacy_stats) {
  return false;
}
void FakeVoiceMediaChannel::SetRawAudioSink(
    uint32_t ssrc,
    std::unique_ptr<webrtc::AudioSinkInterface> sink) {
  sink_ = std::move(sink);
}
void FakeVoiceMediaChannel::SetDefaultRawAudioSink(
    std::unique_ptr<webrtc::AudioSinkInterface> sink) {
  sink_ = std::move(sink);
}
std::vector<webrtc::RtpSource> FakeVoiceMediaChannel::GetSources(
    uint32_t ssrc) const {
  return std::vector<webrtc::RtpSource>();
}
bool FakeVoiceMediaChannel::SetRecvCodecs(
    const std::vector<AudioCodec>& codecs) {
  if (fail_set_recv_codecs()) {
    // Fake the failure in SetRecvCodecs.
    return false;
  }
  recv_codecs_ = codecs;
  return true;
}
bool FakeVoiceMediaChannel::SetSendCodecs(
    const std::vector<AudioCodec>& codecs) {
  if (fail_set_send_codecs()) {
    // Fake the failure in SetSendCodecs.
    return false;
  }
  send_codecs_ = codecs;
  return true;
}
bool FakeVoiceMediaChannel::SetMaxSendBandwidth(int bps) {
  max_bps_ = bps;
  return true;
}
bool FakeVoiceMediaChannel::SetOptions(const AudioOptions& options) {
  // Does a "merge" of current options and set options.
  options_.SetAll(options);
  return true;
}
bool FakeVoiceMediaChannel::SetLocalSource(uint32_t ssrc, AudioSource* source) {
  auto it = local_sinks_.find(ssrc);
  if (source) {
    if (it != local_sinks_.end()) {
      RTC_CHECK(it->second->source() == source);
    } else {
      local_sinks_.insert(std::make_pair(
          ssrc, std::make_unique<VoiceChannelAudioSink>(source)));
    }
  } else {
    if (it != local_sinks_.end()) {
      local_sinks_.erase(it);
    }
  }
  return true;
}

bool CompareDtmfInfo(const FakeVoiceMediaChannel::DtmfInfo& info,
                     uint32_t ssrc,
                     int event_code,
                     int duration) {
  return (info.duration == duration && info.event_code == event_code &&
          info.ssrc == ssrc);
}

FakeVideoMediaChannel::FakeVideoMediaChannel(FakeVideoEngine* engine,
                                             const VideoOptions& options,
                                             TaskQueueBase* network_thread)
    : RtpHelper<VideoMediaChannel>(network_thread),
      engine_(engine),
      max_bps_(-1) {
  SetOptions(options);
}
FakeVideoMediaChannel::~FakeVideoMediaChannel() {
  if (engine_) {
    engine_->UnregisterChannel(this);
  }
}
const std::vector<VideoCodec>& FakeVideoMediaChannel::recv_codecs() const {
  return recv_codecs_;
}
const std::vector<VideoCodec>& FakeVideoMediaChannel::send_codecs() const {
  return send_codecs_;
}
const std::vector<VideoCodec>& FakeVideoMediaChannel::codecs() const {
  return send_codecs();
}
bool FakeVideoMediaChannel::rendering() const {
  return playout();
}
const VideoOptions& FakeVideoMediaChannel::options() const {
  return options_;
}
const std::map<uint32_t, rtc::VideoSinkInterface<webrtc::VideoFrame>*>&
FakeVideoMediaChannel::sinks() const {
  return sinks_;
}
int FakeVideoMediaChannel::max_bps() const {
  return max_bps_;
}
bool FakeVideoMediaChannel::SetSendParameters(
    const VideoSendParameters& params) {
  set_send_rtcp_parameters(params.rtcp);
  return (SetSendCodecs(params.codecs) &&
          SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
          SetSendRtpHeaderExtensions(params.extensions) &&
          SetMaxSendBandwidth(params.max_bandwidth_bps));
}
bool FakeVideoMediaChannel::SetRecvParameters(
    const VideoRecvParameters& params) {
  set_recv_rtcp_parameters(params.rtcp);
  return (SetRecvCodecs(params.codecs) &&
          SetRecvRtpHeaderExtensions(params.extensions));
}
bool FakeVideoMediaChannel::AddSendStream(const StreamParams& sp) {
  return RtpHelper<VideoMediaChannel>::AddSendStream(sp);
}
bool FakeVideoMediaChannel::RemoveSendStream(uint32_t ssrc) {
  return RtpHelper<VideoMediaChannel>::RemoveSendStream(ssrc);
}
bool FakeVideoMediaChannel::GetSendCodec(VideoCodec* send_codec) {
  if (send_codecs_.empty()) {
    return false;
  }
  *send_codec = send_codecs_[0];
  return true;
}
bool FakeVideoMediaChannel::SetSink(
    uint32_t ssrc,
    rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
  auto it = sinks_.find(ssrc);
  if (it == sinks_.end()) {
    return false;
  }
  it->second = sink;
  return true;
}
void FakeVideoMediaChannel::SetDefaultSink(
    rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {}
bool FakeVideoMediaChannel::HasSink(uint32_t ssrc) const {
  return sinks_.find(ssrc) != sinks_.end() && sinks_.at(ssrc) != nullptr;
}
bool FakeVideoMediaChannel::SetSend(bool send) {
  return set_sending(send);
}
bool FakeVideoMediaChannel::SetVideoSend(
    uint32_t ssrc,
    const VideoOptions* options,
    rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
  if (options) {
    if (!SetOptions(*options)) {
      return false;
    }
  }
  sources_[ssrc] = source;
  return true;
}
bool FakeVideoMediaChannel::HasSource(uint32_t ssrc) const {
  return sources_.find(ssrc) != sources_.end() && sources_.at(ssrc) != nullptr;
}
bool FakeVideoMediaChannel::AddRecvStream(const StreamParams& sp) {
  if (!RtpHelper<VideoMediaChannel>::AddRecvStream(sp))
    return false;
  sinks_[sp.first_ssrc()] = NULL;
  output_delays_[sp.first_ssrc()] = 0;
  return true;
}
bool FakeVideoMediaChannel::RemoveRecvStream(uint32_t ssrc) {
  if (!RtpHelper<VideoMediaChannel>::RemoveRecvStream(ssrc))
    return false;
  sinks_.erase(ssrc);
  output_delays_.erase(ssrc);
  return true;
}
void FakeVideoMediaChannel::FillBitrateInfo(BandwidthEstimationInfo* bwe_info) {
}
bool FakeVideoMediaChannel::GetStats(VideoMediaInfo* info) {
  return false;
}
std::vector<webrtc::RtpSource> FakeVideoMediaChannel::GetSources(
    uint32_t ssrc) const {
  return {};
}
bool FakeVideoMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
                                                         int delay_ms) {
  if (output_delays_.find(ssrc) == output_delays_.end()) {
    return false;
  } else {
    output_delays_[ssrc] = delay_ms;
    return true;
  }
}
absl::optional<int> FakeVideoMediaChannel::GetBaseMinimumPlayoutDelayMs(
    uint32_t ssrc) const {
  const auto it = output_delays_.find(ssrc);
  if (it != output_delays_.end()) {
    return it->second;
  }
  return absl::nullopt;
}
bool FakeVideoMediaChannel::SetRecvCodecs(
    const std::vector<VideoCodec>& codecs) {
  if (fail_set_recv_codecs()) {
    // Fake the failure in SetRecvCodecs.
    return false;
  }
  recv_codecs_ = codecs;
  return true;
}
bool FakeVideoMediaChannel::SetSendCodecs(
    const std::vector<VideoCodec>& codecs) {
  if (fail_set_send_codecs()) {
    // Fake the failure in SetSendCodecs.
    return false;
  }
  send_codecs_ = codecs;

  return true;
}
bool FakeVideoMediaChannel::SetOptions(const VideoOptions& options) {
  options_ = options;
  return true;
}

bool FakeVideoMediaChannel::SetMaxSendBandwidth(int bps) {
  max_bps_ = bps;
  return true;
}

void FakeVideoMediaChannel::SetRecordableEncodedFrameCallback(
    uint32_t ssrc,
    std::function<void(const webrtc::RecordableEncodedFrame&)> callback) {}

void FakeVideoMediaChannel::ClearRecordableEncodedFrameCallback(uint32_t ssrc) {
}

void FakeVideoMediaChannel::GenerateKeyFrame(uint32_t ssrc) {}

FakeVoiceEngine::FakeVoiceEngine() : fail_create_channel_(false) {
  // Add a fake audio codec. Note that the name must not be "" as there are
  // sanity checks against that.
  SetCodecs({AudioCodec(101, "fake_audio_codec", 0, 0, 1)});
}
void FakeVoiceEngine::Init() {}
rtc::scoped_refptr<webrtc::AudioState> FakeVoiceEngine::GetAudioState() const {
  return rtc::scoped_refptr<webrtc::AudioState>();
}
VoiceMediaChannel* FakeVoiceEngine::CreateMediaChannel(
    webrtc::Call* call,
    const MediaConfig& config,
    const AudioOptions& options,
    const webrtc::CryptoOptions& crypto_options) {
  if (fail_create_channel_) {
    return nullptr;
  }

  FakeVoiceMediaChannel* ch =
      new FakeVoiceMediaChannel(this, options, call->network_thread());
  channels_.push_back(ch);
  return ch;
}
FakeVoiceMediaChannel* FakeVoiceEngine::GetChannel(size_t index) {
  return (channels_.size() > index) ? channels_[index] : NULL;
}
void FakeVoiceEngine::UnregisterChannel(VoiceMediaChannel* channel) {
  channels_.erase(absl::c_find(channels_, channel));
}
const std::vector<AudioCodec>& FakeVoiceEngine::send_codecs() const {
  return send_codecs_;
}
const std::vector<AudioCodec>& FakeVoiceEngine::recv_codecs() const {
  return recv_codecs_;
}
void FakeVoiceEngine::SetCodecs(const std::vector<AudioCodec>& codecs) {
  send_codecs_ = codecs;
  recv_codecs_ = codecs;
}
void FakeVoiceEngine::SetRecvCodecs(const std::vector<AudioCodec>& codecs) {
  recv_codecs_ = codecs;
}
void FakeVoiceEngine::SetSendCodecs(const std::vector<AudioCodec>& codecs) {
  send_codecs_ = codecs;
}
int FakeVoiceEngine::GetInputLevel() {
  return 0;
}
bool FakeVoiceEngine::StartAecDump(webrtc::FileWrapper file,
                                   int64_t max_size_bytes) {
  return false;
}
void FakeVoiceEngine::StopAecDump() {}

std::vector<webrtc::RtpHeaderExtensionCapability>
FakeVoiceEngine::GetRtpHeaderExtensions() const {
  return header_extensions_;
}

void FakeVoiceEngine::SetRtpHeaderExtensions(
    std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
  header_extensions_ = std::move(header_extensions);
}

FakeVideoEngine::FakeVideoEngine()
    : capture_(false), fail_create_channel_(false) {
  // Add a fake video codec. Note that the name must not be "" as there are
  // sanity checks against that.
  send_codecs_.push_back(VideoCodec(0, "fake_video_codec"));
  recv_codecs_.push_back(VideoCodec(0, "fake_video_codec"));
}
bool FakeVideoEngine::SetOptions(const VideoOptions& options) {
  options_ = options;
  return true;
}
VideoMediaChannel* FakeVideoEngine::CreateMediaChannel(
    webrtc::Call* call,
    const MediaConfig& config,
    const VideoOptions& options,
    const webrtc::CryptoOptions& crypto_options,
    webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) {
  if (fail_create_channel_) {
    return nullptr;
  }

  FakeVideoMediaChannel* ch =
      new FakeVideoMediaChannel(this, options, call->network_thread());
  channels_.emplace_back(ch);
  return ch;
}
FakeVideoMediaChannel* FakeVideoEngine::GetChannel(size_t index) {
  return (channels_.size() > index) ? channels_[index] : nullptr;
}
void FakeVideoEngine::UnregisterChannel(VideoMediaChannel* channel) {
  auto it = absl::c_find(channels_, channel);
  RTC_DCHECK(it != channels_.end());
  channels_.erase(it);
}
std::vector<VideoCodec> FakeVideoEngine::send_codecs(bool use_rtx) const {
  return send_codecs_;
}

std::vector<VideoCodec> FakeVideoEngine::recv_codecs(bool use_rtx) const {
  return recv_codecs_;
}

void FakeVideoEngine::SetSendCodecs(const std::vector<VideoCodec>& codecs) {
  send_codecs_ = codecs;
}

void FakeVideoEngine::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
  recv_codecs_ = codecs;
}

bool FakeVideoEngine::SetCapture(bool capture) {
  capture_ = capture;
  return true;
}
std::vector<webrtc::RtpHeaderExtensionCapability>
FakeVideoEngine::GetRtpHeaderExtensions() const {
  return header_extensions_;
}
void FakeVideoEngine::SetRtpHeaderExtensions(
    std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
  header_extensions_ = std::move(header_extensions);
}

FakeMediaEngine::FakeMediaEngine()
    : CompositeMediaEngine(std::make_unique<FakeVoiceEngine>(),
                           std::make_unique<FakeVideoEngine>()),
      voice_(static_cast<FakeVoiceEngine*>(&voice())),
      video_(static_cast<FakeVideoEngine*>(&video())) {}
FakeMediaEngine::~FakeMediaEngine() {}
void FakeMediaEngine::SetAudioCodecs(const std::vector<AudioCodec>& codecs) {
  voice_->SetCodecs(codecs);
}
void FakeMediaEngine::SetAudioRecvCodecs(
    const std::vector<AudioCodec>& codecs) {
  voice_->SetRecvCodecs(codecs);
}
void FakeMediaEngine::SetAudioSendCodecs(
    const std::vector<AudioCodec>& codecs) {
  voice_->SetSendCodecs(codecs);
}
void FakeMediaEngine::SetVideoCodecs(const std::vector<VideoCodec>& codecs) {
  video_->SetSendCodecs(codecs);
  video_->SetRecvCodecs(codecs);
}

FakeVoiceMediaChannel* FakeMediaEngine::GetVoiceChannel(size_t index) {
  return voice_->GetChannel(index);
}
FakeVideoMediaChannel* FakeMediaEngine::GetVideoChannel(size_t index) {
  return video_->GetChannel(index);
}

void FakeMediaEngine::set_fail_create_channel(bool fail) {
  voice_->fail_create_channel_ = fail;
  video_->fail_create_channel_ = fail;
}

}  // namespace cricket
