defmodule Membrane.Tee do
  @moduledoc """
  Element for forwarding buffers to at least one output pad

  It has one input pad `:input` and 2 output pads:
  * `:output` - is a dynamic pad which is always available and works in pull mode
  * `:push_output` - is a dynamic pad that can be linked to any number of elements (including 0) and works
    in push mode

  The `:output` pads dictate the speed of processing data and any element (or elements) connected to
  `:push_output` pad will receive the same data as all `:output` instances.
  """
  use Membrane.Filter, flow_control_hints?: false

  require Membrane.Logger

  def_input_pad :input,
    availability: :always,
    flow_control: :auto,
    accepted_format: _any

  def_output_pad :output,
    availability: :on_request,
    flow_control: :auto,
    accepted_format: _any

  def_output_pad :push_output,
    availability: :on_request,
    flow_control: :push,
    accepted_format: _any

  @impl true
  def handle_init(_ctx, _opts) do
    {[], %{stream_format: nil}}
  end

  @impl true
  def handle_playing(ctx, state) do
    if map_size(ctx.pads) < 2 do
      Membrane.Logger.debug("""
      #{inspect(__MODULE__)} enters :playing playback without any output (:output or :push_output) \
      pads linked.
      """)
    end

    {[], state}
  end

  @impl true
  def handle_stream_format(:input, stream_format, _ctx, state) do
    {[forward: stream_format], %{state | stream_format: stream_format}}
  end

  @impl true
  def handle_pad_added(Pad.ref(name, _ref) = output_pad, ctx, state)
      when name in [:output, :push_output] do
    maybe_stream_format =
      case state.stream_format do
        nil -> []
        stream_format -> [stream_format: {output_pad, stream_format}]
      end

    maybe_eos =
      if ctx.pads.input.end_of_stream?,
        do: [end_of_stream: output_pad],
        else: []

    {maybe_stream_format ++ maybe_eos, state}
  end

  @impl true
  def handle_buffer(:input, buffer, _ctx, state) do
    {[forward: buffer], state}
  end
end
