text: # Copyright (c) 2022 Tulir Asokan
  #
  # This Source Code Form is subject to the terms of the Mozilla Public
  # License, v. 2.0. If a copy of the MPL was not distributed with this
  # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  from typing import Generic, List, Optional, TypeVar, Union
  
  from attr import dataclass
  import attr
  
  from ..primitive import JSON, UserID
  from ..util import ExtensibleEnum, SerializableAttrs
  from .base import BaseRoomEvent
  from .type import EventType
  
  
  class CallDataType(ExtensibleEnum):
      OFFER = "offer"
      ANSWER = "answer"
  
  
  class CallHangupReason(ExtensibleEnum):
      ICE_FAILED = "ice_failed"
      INVITE_TIMEOUT = "invite_timeout"
      USER_HANGUP = "user_hangup"
      USER_MEDIA_FAILED = "user_media_failed"
      UNKNOWN_ERROR = "unknown_error"
  
  
  @dataclass
  class CallData(SerializableAttrs):
      sdp: str
      type: CallDataType
  
  
  @dataclass
  class CallCandidate(SerializableAttrs):
      candidate: str
      sdp_m_line_index: int = attr.ib(metadata={"json": "sdpMLineIndex"}, default=None)
      sdp_mid: str = attr.ib(metadata={"json": "sdpMid"}, default=None)
  
  
  @dataclass
  class CallInviteEventContent(SerializableAttrs):
      call_id: str
      lifetime: int
      version: int
      offer: CallData
      party_id: Optional[str] = None
      invitee: Optional[UserID] = None
  
  
  @dataclass
  class CallCandidatesEventContent(SerializableAttrs):
      call_id: str
      version: int
      candidates: List[CallCandidate]
      party_id: Optional[str] = None
  
  
  @dataclass
  class CallSelectAnswerEventContent(SerializableAttrs):
      call_id: str
      version: int
      party_id: str
      selected_party_id: str
  
  
  @dataclass
  class CallAnswerEventContent(SerializableAttrs):
      call_id: str
      version: int
      answer: CallData
      party_id: Optional[str] = None
  
  
  @dataclass
  class CallHangupEventContent(SerializableAttrs):
      call_id: str
      version: int
      reason: CallHangupReason = CallHangupReason.USER_HANGUP
      party_id: Optional[str] = None
  
  
  @dataclass
  class CallRejectEventContent(SerializableAttrs):
      call_id: str
      version: int
      party_id: str
  
  
  @dataclass
  class CallNegotiateEventContent(SerializableAttrs):
      call_id: str
      version: int
      lifetime: int
      party_id: str
      description: CallData
  
  
  type_to_class = {
      EventType.CALL_INVITE: CallInviteEventContent,
      EventType.CALL_CANDIDATES: CallCandidatesEventContent,
      EventType.CALL_SELECT_ANSWER: CallSelectAnswerEventContent,
      EventType.CALL_ANSWER: CallAnswerEventContent,
      EventType.CALL_HANGUP: CallHangupEventContent,
      EventType.CALL_NEGOTIATE: CallNegotiateEventContent,
      EventType.CALL_REJECT: CallRejectEventContent,
  }
  
  CallEventContent = Union[
      CallInviteEventContent,
      CallCandidatesEventContent,
      CallAnswerEventContent,
      CallSelectAnswerEventContent,
      CallHangupEventContent,
      CallNegotiateEventContent,
      CallRejectEventContent,
  ]
  
  T = TypeVar("T", bound=CallEventContent)
  
  
  @dataclass
  class CallEvent(BaseRoomEvent, SerializableAttrs, Generic[T]):
      content: T
  
      @classmethod
      def deserialize(cls, data: JSON, event_type: Optional[EventType] = None) -> "CallEvent":
          event_type = event_type or EventType.find(data.get("type"))
          data["content"] = type_to_class[event_type].deserialize(data["content"])
          return super().deserialize(data)
  