/*
 *  Copyright 2025 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.
 */

#ifndef PC_CODEC_VENDOR_H_
#define PC_CODEC_VENDOR_H_

#include <utility>
#include <vector>

#include "absl/base/nullability.h"
#include "absl/strings/string_view.h"
#include "api/field_trials_view.h"
#include "api/media_types.h"
#include "api/rtc_error.h"
#include "api/rtp_transceiver_direction.h"
#include "api/sequence_checker.h"
#include "call/payload_type.h"
#include "media/base/codec.h"
#include "media/base/codec_list.h"
#include "media/base/media_engine.h"
#include "pc/media_options.h"
#include "pc/session_description.h"
#include "pc/typed_codec_vendor.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"

namespace webrtc {

// This class contains the functions required to compute the list of codecs
// for SDP offer/answer. It is exposed to MediaSessionDescriptionFactory
// for the construction of offers and answers.

// TODO: bugs.webrtc.org/360058654 - complete the architectural changes
// The list of things to be done:
// - Make as much as possible private.
// - Make state const where possible while updates related to threading are
// being done.
// - Remove test code from the implementation.
// - Split object usage into four objects: sender/receiver/audio/video.
// - Remove audio/video from the call names, merge code where possible.
// - Make the class instances owned by transceivers, so that codec
//   lists can differ per transceiver.
// For cleanliness:
// - Thread guard
// For performance:
// - Ensure that no blocking calls are made.
class CodecVendor {
 public:
  // A null media_engine is permitted in order to allow unit testing where the
  // codecs are explicitly set by the test.
  // TODO: bugs.webrtc.org/360058654 - The tests can accomplish what they need
  // by using the same interface as is used in production.
  // Update the tests instead to supply a valid MediaEngineInterface object
  // and rather test how CodecVendor works regularly.
  CodecVendor(const MediaEngineInterface* absl_nullable media_engine,
              bool rtx_enabled,
              const FieldTrialsView& trials);

  RTCErrorOr<std::vector<Codec>> GetNegotiatedCodecsForOffer(
      const MediaDescriptionOptions& media_description_options,
      const MediaSessionOptions& session_options,
      const ContentInfo* current_content,
      PayloadTypeSuggester& pt_suggester);

  RTCErrorOr<Codecs> GetNegotiatedCodecsForAnswer(
      const MediaDescriptionOptions& media_description_options,
      const MediaSessionOptions& session_options,
      RtpTransceiverDirection offer_rtd,
      RtpTransceiverDirection answer_rtd,
      const ContentInfo* current_content,
      std::vector<Codec> codecs_from_offer,
      PayloadTypeSuggester& pt_suggester);

  // Function exposed for issues.webrtc.org/412904801
  // Modify the video codecs to return on subsequent GetNegotiated* calls.
  // The input is a vector of pairs of codecs.
  // For each pair, the first element is the codec to be replaced,
  // and the second element is the codec to replace it with.
  void ModifyVideoCodecs(const std::vector<std::pair<Codec, Codec>>& changes);

  // Workaround for issues.webrtc.org/412904801.
  // Specifically sets packetization to "raw" for the matching codec in
  // configurations_, to support the packetization=raw hack applied via SDP
  // munging.
  void SetRawPacketization(const Codec& codec);

  bool payload_types_in_transport() const {
    return payload_types_in_transport_;
  }

  // Functions exposed for testing
  CodecList audio_sendrecv_codecs() const;
  const CodecList& audio_send_codecs() const;
  const CodecList& audio_recv_codecs() const;
  CodecList video_sendrecv_codecs() const;
  const CodecList& video_send_codecs() const;
  const CodecList& video_recv_codecs() const;

 private:
  CodecList GetAudioCodecsForOffer(
      const RtpTransceiverDirection& direction) const;
  CodecList GetAudioCodecsForAnswer(
      const RtpTransceiverDirection& offer,
      const RtpTransceiverDirection& answer) const;
  CodecList GetVideoCodecsForOffer(
      const RtpTransceiverDirection& direction) const;
  CodecList GetVideoCodecsForAnswer(
      const RtpTransceiverDirection& offer,
      const RtpTransceiverDirection& answer) const;

  RTCError MergeCodecsByDirection(MediaType type,
                                  RtpTransceiverDirection direction,
                                  absl::string_view mid,
                                  CodecList& codecs_out,
                                  PayloadTypeSuggester& pt_suggester,
                                  bool pick_from_top_of_range);

  // Makes sure that modifications and reading data is done on the same thread
  // and to makessure we consistently make calls to GetNegotiatedCodecsForOffer
  // and GetNegotiatedCodecsForAnswer in the same calling context.
  RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;

  const FieldTrialsView& trials_;

  const TypedCodecVendor audio_send_codecs_;
  const TypedCodecVendor audio_recv_codecs_;

  const bool payload_types_in_transport_;

  // TODO: bugs.webrtc.org/412904801 - Make const. In order to be able to do
  // that, `ModifyVideoCodecs` needs to be removed. In the meantime, codec
  // information must be read and modified on the same task queue.
  TypedCodecVendor video_send_codecs_ RTC_GUARDED_BY(sequence_checker_);
  TypedCodecVendor video_recv_codecs_ RTC_GUARDED_BY(sequence_checker_);
};

// A class to assist in looking up data for a codec mapping.
// Pure virtual to allow implementations that depend on things that
// codec_vendor.h should not depend on.
// Pointers returned are not stable, and should not be stored.
class CodecLookupHelper {
 public:
  virtual ~CodecLookupHelper() = default;
  virtual ::webrtc::PayloadTypeSuggester* PayloadTypeSuggester() = 0;
  // Look up the codec vendor to use, depending on context.
  // This call may get additional arguments in the future, to aid
  // in selection of the correct context.
  virtual CodecVendor* GetCodecVendor() = 0;
};

// A helper function to merge codecs numbered in one PT numberspace
// into a list numbered in another PT numberspace. Exposed for testing.
// This function is only available for testing the legacy path.
RTCError MergeCodecsForTesting(const CodecList& reference_codecs,
                               absl::string_view mid,
                               CodecList& offered_codecs,
                               PayloadTypeSuggester& pt_suggester,
                               bool pick_from_top_of_range = false);

}  //  namespace webrtc

#endif  // PC_CODEC_VENDOR_H_
