/*
 *  Copyright (c) 2014 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/media/engine/webrtcmediaengine.h"
#include "webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.h"

#include <algorithm>

#ifdef HAVE_WEBRTC_VIDEO
#include "webrtc/media/engine/webrtcvideoengine2.h"
#else
#include "webrtc/media/engine/nullwebrtcvideoengine.h"
#endif
#include "webrtc/media/engine/webrtcvoiceengine.h"

namespace cricket {

class WebRtcMediaEngine2
#ifdef HAVE_WEBRTC_VIDEO
    : public CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine2> {
#else
    : public CompositeMediaEngine<WebRtcVoiceEngine, NullWebRtcVideoEngine> {
#endif
 public:
  WebRtcMediaEngine2(webrtc::AudioDeviceModule* adm,
                     const rtc::scoped_refptr<webrtc::AudioDecoderFactory>&
                         audio_decoder_factory,
                     WebRtcVideoEncoderFactory* video_encoder_factory,
                     WebRtcVideoDecoderFactory* video_decoder_factory)
#ifdef HAVE_WEBRTC_VIDEO
      : CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine2>(
            adm, audio_decoder_factory){
#else
      : CompositeMediaEngine<WebRtcVoiceEngine, NullWebRtcVideoEngine>(
            adm, audio_decoder_factory) {
#endif
    video_.SetExternalDecoderFactory(video_decoder_factory);
    video_.SetExternalEncoderFactory(video_encoder_factory);
  }
};

}  // namespace cricket

cricket::MediaEngineInterface* CreateWebRtcMediaEngine(
    webrtc::AudioDeviceModule* adm,
    const rtc::scoped_refptr<webrtc::AudioDecoderFactory>&
        audio_decoder_factory,
    cricket::WebRtcVideoEncoderFactory* video_encoder_factory,
    cricket::WebRtcVideoDecoderFactory* video_decoder_factory) {
  return new cricket::WebRtcMediaEngine2(
      adm, audio_decoder_factory, video_encoder_factory, video_decoder_factory);
}

void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) {
  delete media_engine;
}

namespace cricket {

// TODO(ossu): Backwards-compatible interface. Will be deprecated once the
// audio decoder factory is fully plumbed and used throughout WebRTC.
// See: crbug.com/webrtc/6000
MediaEngineInterface* WebRtcMediaEngineFactory::Create(
    webrtc::AudioDeviceModule* adm,
    WebRtcVideoEncoderFactory* video_encoder_factory,
    WebRtcVideoDecoderFactory* video_decoder_factory) {
  return CreateWebRtcMediaEngine(adm,
                                 webrtc::CreateBuiltinAudioDecoderFactory(),
                                 video_encoder_factory, video_decoder_factory);
}

// Used by PeerConnectionFactory to create a media engine passed into
// ChannelManager.
MediaEngineInterface* WebRtcMediaEngineFactory::Create(
    webrtc::AudioDeviceModule* adm,
    const rtc::scoped_refptr<webrtc::AudioDecoderFactory>&
        audio_decoder_factory,
    WebRtcVideoEncoderFactory* video_encoder_factory,
    WebRtcVideoDecoderFactory* video_decoder_factory) {
  return CreateWebRtcMediaEngine(adm, audio_decoder_factory,
                                 video_encoder_factory, video_decoder_factory);
}

namespace {
// Remove mutually exclusive extensions with lower priority.
void DiscardRedundantExtensions(
    std::vector<webrtc::RtpExtension>* extensions,
    rtc::ArrayView<const char*> extensions_decreasing_prio) {
  RTC_DCHECK(extensions);
  bool found = false;
  for (const char* uri : extensions_decreasing_prio) {
    auto it = std::find_if(
        extensions->begin(), extensions->end(),
        [uri](const webrtc::RtpExtension& rhs) { return rhs.uri == uri; });
    if (it != extensions->end()) {
      if (found) {
        extensions->erase(it);
      }
      found = true;
    }
  }
}
}  // namespace

bool ValidateRtpExtensions(
    const std::vector<webrtc::RtpExtension>& extensions) {
  bool id_used[14] = {false};
  for (const auto& extension : extensions) {
    if (extension.id <= 0 || extension.id >= 15) {
      LOG(LS_ERROR) << "Bad RTP extension ID: " << extension.ToString();
      return false;
    }
    if (id_used[extension.id - 1]) {
      LOG(LS_ERROR) << "Duplicate RTP extension ID: " << extension.ToString();
      return false;
    }
    id_used[extension.id - 1] = true;
  }
  return true;
}

std::vector<webrtc::RtpExtension> FilterRtpExtensions(
    const std::vector<webrtc::RtpExtension>& extensions,
    bool (*supported)(const std::string&),
    bool filter_redundant_extensions) {
  RTC_DCHECK(ValidateRtpExtensions(extensions));
  RTC_DCHECK(supported);
  std::vector<webrtc::RtpExtension> result;

  // Ignore any extensions that we don't recognize.
  for (const auto& extension : extensions) {
    if (supported(extension.uri)) {
      result.push_back(extension);
    } else {
      LOG(LS_WARNING) << "Unsupported RTP extension: " << extension.ToString();
    }
  }

  // Sort by name, ascending, so that we don't reset extensions if they were
  // specified in a different order (also allows us to use std::unique below).
  std::sort(result.begin(), result.end(),
            [](const webrtc::RtpExtension& rhs,
               const webrtc::RtpExtension& lhs) { return rhs.uri < lhs.uri; });

  // Remove unnecessary extensions (used on send side).
  if (filter_redundant_extensions) {
    auto it = std::unique(
        result.begin(), result.end(),
        [](const webrtc::RtpExtension& rhs, const webrtc::RtpExtension& lhs) {
          return rhs.uri == lhs.uri;
        });
    result.erase(it, result.end());

    // Keep just the highest priority extension of any in the following list.
    static const char* kBweExtensionPriorities[] = {
        webrtc::RtpExtension::kTransportSequenceNumberUri,
        webrtc::RtpExtension::kAbsSendTimeUri,
        webrtc::RtpExtension::kTimestampOffsetUri};
    DiscardRedundantExtensions(&result, kBweExtensionPriorities);
  }

  return result;
}
}  // namespace cricket