defmodule Membrane.RTP.StreamReceiveBin do
  @moduledoc """
  This bin gets a parsed RTP stream on input and outputs raw media stream.

  Its responsibility is to depayload the RTP stream and compensate the

  use Membrane.Bin

  alias Membrane.{RTCP, RTP, SRTP}

  def_options srtp_policies: [
                spec: [ExLibSRTP.Policy.t()],
                default: []
              secure?: [
                spec: boolean(),
                default: false
              extensions: [
                spec: [RTP.SessionBin.extension_t()],
                default: []
              clock_rate: [
                spec: RTP.clock_rate_t()
              depayloader: [spec: module() | nil],
              local_ssrc: [spec: RTP.ssrc_t()],
              remote_ssrc: [spec: RTP.ssrc_t()],
              rtcp_report_interval: [spec: Membrane.Time.t() | nil],
              telemetry_label: [
                spec: [{atom(), any()}],
                default: []

  def_input_pad :input, accepted_format: _any, demand_unit: :buffers
  def_output_pad :output, accepted_format: _any, demand_unit: :buffers

  @impl true
  def handle_init(_ctx, opts) do
    if and not Code.ensure_loaded?(ExLibSRTP),
      do: raise("Optional dependency :ex_libsrtp is required when using secure? option")

    add_decryptor =
      &child(&1, :decryptor, struct(SRTP.Decryptor, %{policies: opts.srtp_policies}))

    add_depayloader_bin =
      &child(&1, :depayloader, %RTP.DepayloaderBin{
        depayloader: opts.depayloader,
        clock_rate: opts.clock_rate

    structure =
      |> add_extensions(opts.extensions)
      |> child(:rtcp_receiver, %RTCP.Receiver{
        local_ssrc: opts.local_ssrc,
        remote_ssrc: opts.remote_ssrc,
        report_interval: opts.rtcp_report_interval,
        telemetry_label: opts.telemetry_label
      |> child(:packet_tracker, %RTP.InboundPacketTracker{
        clock_rate: opts.clock_rate,
        repair_sequence_numbers?: true
      |> then(if, do: add_decryptor, else: & &1)
      |> then(if opts.depayloader, do: add_depayloader_bin, else: & &1)
      |> bin_output()

    {[spec: structure], %{}}

  defp add_extensions(link_builder, extensions) do
    Enum.reduce(extensions, link_builder, fn {extension_name, extension}, builder ->
      builder |> child(extension_name, extension)