lib/jellyfish/notification.ex

defmodule Jellyfish.Notification do
  @moduledoc false

  alias Jellyfish.{Component, Peer, Room, Track}
  alias Jellyfish.ServerMessage.{TrackAdded, TrackMetadataUpdated, TrackRemoved}

  defmodule RoomCreated do
    @moduledoc nil

    @enforce_keys [:room_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id()
          }
  end

  defmodule RoomDeleted do
    @moduledoc nil

    @enforce_keys [:room_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id()
          }
  end

  defmodule RoomCrashed do
    @moduledoc nil

    @enforce_keys [:room_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id()
          }
  end

  defmodule PeerAdded do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id()
          }
  end

  defmodule PeerDeleted do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id()
          }
  end

  defmodule PeerConnected do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id()
          }
  end

  defmodule PeerDisconnected do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id()
          }
  end

  defmodule PeerMetadataUpdated do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id, :metadata]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id(),
            metadata: any()
          }
  end

  defmodule PeerCrashed do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id()
          }
  end

  defmodule ComponentCrashed do
    @moduledoc nil

    @enforce_keys [:room_id, :component_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            component_id: Component.id()
          }
  end

  defmodule HlsPlayable do
    @moduledoc nil

    @enforce_keys [:room_id, :component_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            component_id: Component.id()
          }
  end

  defmodule HlsUploaded do
    @moduledoc nil

    @enforce_keys [:room_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id()
          }
  end

  defmodule HlsUploadCrashed do
    @moduledoc nil

    @enforce_keys [:room_id]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id()
          }
  end

  defmodule ComponentTrackAdded do
    @moduledoc nil

    @enforce_keys [:room_id, :component_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            component_id: Component.id(),
            track: Track.t()
          }
  end

  defmodule ComponentTrackMetadataUpdated do
    @moduledoc nil

    @enforce_keys [:room_id, :component_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            component_id: Component.id(),
            track: Track.t()
          }
  end

  defmodule ComponentTrackRemoved do
    @moduledoc nil

    @enforce_keys [:room_id, :component_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            component_id: Component.id(),
            track: Track.t()
          }
  end

  defmodule PeerTrackAdded do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id(),
            track: Track.t()
          }
  end

  defmodule PeerTrackMetadataUpdated do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id(),
            track: Track.t()
          }
  end

  defmodule PeerTrackRemoved do
    @moduledoc nil

    @enforce_keys [:room_id, :peer_id, :track]
    defstruct @enforce_keys

    @type t :: %__MODULE__{
            room_id: Room.id(),
            peer_id: Peer.id(),
            track: Track.t()
          }
  end

  @discarded_fields [:__unknown_fields__]

  @doc false
  def to_notification(%module{} = message)
      when module in [TrackAdded, TrackMetadataUpdated, TrackRemoved] do
    {endpoint_type, endpoint_id} = message.endpoint_info

    notification_module =
      case {endpoint_type, module} do
        {:component_id, TrackAdded} -> ComponentTrackAdded
        {:component_id, TrackMetadataUpdated} -> ComponentTrackMetadataUpdated
        {:component_id, TrackRemoved} -> ComponentTrackRemoved
        {:peer_id, TrackAdded} -> PeerTrackAdded
        {:peer_id, TrackMetadataUpdated} -> PeerTrackMetadataUpdated
        {:peer_id, TrackRemoved} -> PeerTrackRemoved
      end

    discarded_fields = @discarded_fields ++ [:endpoint_info]

    message
    |> Map.from_struct()
    |> Map.drop(discarded_fields)
    |> Map.merge(%{
      endpoint_type => endpoint_id,
      :track => from_proto_track(message.track)
    })
    |> then(&struct!(notification_module, &1))
  end

  def to_notification(%module{} = message) do
    notification_module =
      module
      |> Module.split()
      |> List.last()
      |> then(&Module.concat(__MODULE__, &1))

    message
    |> Map.from_struct()
    |> Map.drop(@discarded_fields)
    |> then(&struct!(notification_module, &1))
  end

  defp from_proto_track(track) do
    %Track{
      id: track.id,
      type: from_proto_track_type(track.type),
      metadata: Jason.decode!(track.metadata)
    }
  end

  defp from_proto_track_type(:TRACK_TYPE_VIDEO), do: :video
  defp from_proto_track_type(:TRACK_TYPE_AUDIO), do: :audio
end