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

#ifndef DOM_MEDIA_WEBRTC_SDP_SDPMEDIASECTION_H_
#define DOM_MEDIA_WEBRTC_SDP_SDPMEDIASECTION_H_

#include <ostream>
#include <string>
#include <vector>

#include "sdp/SdpAttributeList.h"
#include "sdp/SdpEnum.h"

namespace mozilla {

class SdpAttributeList;

class SdpConnection;

class SdpMediaSection {
 public:
  enum MediaType { kAudio, kVideo, kText, kApplication, kMessage };
  // don't add to enum to avoid warnings about unhandled enum values
  static const size_t kMediaTypes = static_cast<size_t>(kMessage) + 1;

  enum Protocol {
    kRtpAvp,             // RTP/AVP [RFC4566]
    kUdp,                // udp [RFC4566]
    kVat,                // vat [historic]
    kRtp,                // rtp [historic]
    kUdptl,              // udptl [ITU-T]
    kTcp,                // TCP [RFC4145]
    kRtpAvpf,            // RTP/AVPF [RFC4585]
    kTcpRtpAvp,          // TCP/RTP/AVP [RFC4571]
    kRtpSavp,            // RTP/SAVP [RFC3711]
    kTcpBfcp,            // TCP/BFCP [RFC4583]
    kTcpTlsBfcp,         // TCP/TLS/BFCP [RFC4583]
    kTcpTls,             // TCP/TLS [RFC4572]
    kFluteUdp,           // FLUTE/UDP [RFC-mehta-rmt-flute-sdp-05]
    kTcpMsrp,            // TCP/MSRP [RFC4975]
    kTcpTlsMsrp,         // TCP/TLS/MSRP [RFC4975]
    kDccp,               // DCCP [RFC5762]
    kDccpRtpAvp,         // DCCP/RTP/AVP [RFC5762]
    kDccpRtpSavp,        // DCCP/RTP/SAVP [RFC5762]
    kDccpRtpAvpf,        // DCCP/RTP/AVPF [RFC5762]
    kDccpRtpSavpf,       // DCCP/RTP/SAVPF [RFC5762]
    kRtpSavpf,           // RTP/SAVPF [RFC5124]
    kUdpTlsRtpSavp,      // UDP/TLS/RTP/SAVP [RFC5764]
    kTcpDtlsRtpSavp,     // TCP/DTLS/RTP/SAVP [RFC7850]
    kDccpTlsRtpSavp,     // DCCP/TLS/RTP/SAVP [RFC5764]
    kUdpTlsRtpSavpf,     // UDP/TLS/RTP/SAVPF [RFC5764]
    kTcpDtlsRtpSavpf,    // TCP/DTLS/RTP/SAVPF [RFC7850]
    kDccpTlsRtpSavpf,    // DCCP/TLS/RTP/SAVPF [RFC5764]
    kUdpMbmsFecRtpAvp,   // UDP/MBMS-FEC/RTP/AVP [RFC6064]
    kUdpMbmsFecRtpSavp,  // UDP/MBMS-FEC/RTP/SAVP [RFC6064]
    kUdpMbmsRepair,      // UDP/MBMS-REPAIR [RFC6064]
    kFecUdp,             // FEC/UDP [RFC6364]
    kUdpFec,             // UDP/FEC [RFC6364]
    kTcpMrcpv2,          // TCP/MRCPv2 [RFC6787]
    kTcpTlsMrcpv2,       // TCP/TLS/MRCPv2 [RFC6787]
    kPstn,               // PSTN [RFC7195]
    kUdpTlsUdptl,        // UDP/TLS/UDPTL [RFC7345]
    kSctp,               // SCTP [draft-ietf-mmusic-sctp-sdp-07]
    kDtlsSctp,           // DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-07]
    kUdpDtlsSctp,        // UDP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-21]
    kTcpDtlsSctp         // TCP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-21]
  };

  explicit SdpMediaSection(size_t level) : mLevel(level) {}

  virtual MediaType GetMediaType() const = 0;
  virtual unsigned int GetPort() const = 0;
  virtual void SetPort(unsigned int port) = 0;
  virtual unsigned int GetPortCount() const = 0;
  virtual Protocol GetProtocol() const = 0;
  virtual const SdpConnection& GetConnection() const = 0;
  virtual SdpConnection& GetConnection() = 0;
  virtual uint32_t GetBandwidth(const std::string& type) const = 0;
  virtual const std::vector<std::string>& GetFormats() const = 0;

  std::vector<std::string> GetFormatsForSimulcastVersion(
      size_t simulcastVersion, bool send, bool recv) const;
  virtual const SdpAttributeList& GetAttributeList() const = 0;
  virtual SdpAttributeList& GetAttributeList() = 0;

  virtual SdpDirectionAttribute GetDirectionAttribute() const = 0;

  virtual void Serialize(std::ostream&) const = 0;

  virtual void AddCodec(const std::string& pt, const std::string& name,
                        uint32_t clockrate, uint16_t channels) = 0;
  virtual void ClearCodecs() = 0;

  virtual void AddDataChannel(const std::string& name, uint16_t port,
                              uint16_t streams, uint32_t message_size) = 0;

  size_t GetLevel() const { return mLevel; }

  inline bool IsReceiving() const { return GetDirection() & sdp::kRecv; }

  inline bool IsSending() const { return GetDirection() & sdp::kSend; }

  inline void SetReceiving(const bool receiving) {
    auto direction = GetDirection();
    if (direction & sdp::kSend) {
      SetDirection(receiving ? SdpDirectionAttribute::kSendrecv
                             : SdpDirectionAttribute::kSendonly);
    } else {
      SetDirection(receiving ? SdpDirectionAttribute::kRecvonly
                             : SdpDirectionAttribute::kInactive);
    }
  }

  inline void SetSending(const bool sending) {
    auto direction = GetDirection();
    if (direction & sdp::kRecv) {
      SetDirection(sending ? SdpDirectionAttribute::kSendrecv
                           : SdpDirectionAttribute::kRecvonly);
    } else {
      SetDirection(sending ? SdpDirectionAttribute::kSendonly
                           : SdpDirectionAttribute::kInactive);
    }
  }

  inline void SetDirection(const SdpDirectionAttribute::Direction direction) {
    GetAttributeList().SetAttribute(
        MakeUnique<SdpDirectionAttribute>(direction));
  }

  inline SdpDirectionAttribute::Direction GetDirection() const {
    return GetDirectionAttribute().mValue;
  }

  const SdpFmtpAttributeList::Parameters* FindFmtp(const std::string& pt) const;
  void SetFmtp(const SdpFmtpAttributeList::Fmtp& fmtp);
  void RemoveFmtp(const std::string& pt);
  const SdpRtpmapAttributeList::Rtpmap* FindRtpmap(const std::string& pt) const;
  const SdpSctpmapAttributeList::Sctpmap* GetSctpmap() const;
  uint32_t GetSctpPort() const;
  bool GetMaxMessageSize(uint32_t* size) const;
  bool HasRtcpFb(const std::string& pt, const SdpRtcpFbAttributeList::Type type,
                 const std::string& subType) const;
  SdpRtcpFbAttributeList GetRtcpFbs() const;
  void SetRtcpFbs(const SdpRtcpFbAttributeList& rtcpfbs);
  bool HasFormat(const std::string& format) const {
    return std::find(GetFormats().begin(), GetFormats().end(), format) !=
           GetFormats().end();
  }
  void SetSsrcs(const std::vector<uint32_t>& ssrcs, const std::string& cname);
  void AddMsid(const std::string& id, const std::string& appdata);
  const SdpRidAttributeList::Rid* FindRid(const std::string& id) const;

 private:
  size_t mLevel;
};

inline std::ostream& operator<<(std::ostream& os, const SdpMediaSection& ms) {
  ms.Serialize(os);
  return os;
}

inline std::ostream& operator<<(std::ostream& os,
                                const SdpMediaSection::MediaType t) {
  switch (t) {
    case SdpMediaSection::kAudio:
      return os << "audio";
    case SdpMediaSection::kVideo:
      return os << "video";
    case SdpMediaSection::kText:
      return os << "text";
    case SdpMediaSection::kApplication:
      return os << "application";
    case SdpMediaSection::kMessage:
      return os << "message";
  }
  MOZ_ASSERT(false, "Unknown MediaType");
  return os << "?";
}

inline std::ostream& operator<<(std::ostream& os,
                                const SdpMediaSection::Protocol p) {
  switch (p) {
    case SdpMediaSection::kRtpAvp:
      return os << "RTP/AVP";
    case SdpMediaSection::kUdp:
      return os << "udp";
    case SdpMediaSection::kVat:
      return os << "vat";
    case SdpMediaSection::kRtp:
      return os << "rtp";
    case SdpMediaSection::kUdptl:
      return os << "udptl";
    case SdpMediaSection::kTcp:
      return os << "TCP";
    case SdpMediaSection::kRtpAvpf:
      return os << "RTP/AVPF";
    case SdpMediaSection::kTcpRtpAvp:
      return os << "TCP/RTP/AVP";
    case SdpMediaSection::kRtpSavp:
      return os << "RTP/SAVP";
    case SdpMediaSection::kTcpBfcp:
      return os << "TCP/BFCP";
    case SdpMediaSection::kTcpTlsBfcp:
      return os << "TCP/TLS/BFCP";
    case SdpMediaSection::kTcpTls:
      return os << "TCP/TLS";
    case SdpMediaSection::kFluteUdp:
      return os << "FLUTE/UDP";
    case SdpMediaSection::kTcpMsrp:
      return os << "TCP/MSRP";
    case SdpMediaSection::kTcpTlsMsrp:
      return os << "TCP/TLS/MSRP";
    case SdpMediaSection::kDccp:
      return os << "DCCP";
    case SdpMediaSection::kDccpRtpAvp:
      return os << "DCCP/RTP/AVP";
    case SdpMediaSection::kDccpRtpSavp:
      return os << "DCCP/RTP/SAVP";
    case SdpMediaSection::kDccpRtpAvpf:
      return os << "DCCP/RTP/AVPF";
    case SdpMediaSection::kDccpRtpSavpf:
      return os << "DCCP/RTP/SAVPF";
    case SdpMediaSection::kRtpSavpf:
      return os << "RTP/SAVPF";
    case SdpMediaSection::kUdpTlsRtpSavp:
      return os << "UDP/TLS/RTP/SAVP";
    case SdpMediaSection::kTcpDtlsRtpSavp:
      return os << "TCP/DTLS/RTP/SAVP";
    case SdpMediaSection::kDccpTlsRtpSavp:
      return os << "DCCP/TLS/RTP/SAVP";
    case SdpMediaSection::kUdpTlsRtpSavpf:
      return os << "UDP/TLS/RTP/SAVPF";
    case SdpMediaSection::kTcpDtlsRtpSavpf:
      return os << "TCP/DTLS/RTP/SAVPF";
    case SdpMediaSection::kDccpTlsRtpSavpf:
      return os << "DCCP/TLS/RTP/SAVPF";
    case SdpMediaSection::kUdpMbmsFecRtpAvp:
      return os << "UDP/MBMS-FEC/RTP/AVP";
    case SdpMediaSection::kUdpMbmsFecRtpSavp:
      return os << "UDP/MBMS-FEC/RTP/SAVP";
    case SdpMediaSection::kUdpMbmsRepair:
      return os << "UDP/MBMS-REPAIR";
    case SdpMediaSection::kFecUdp:
      return os << "FEC/UDP";
    case SdpMediaSection::kUdpFec:
      return os << "UDP/FEC";
    case SdpMediaSection::kTcpMrcpv2:
      return os << "TCP/MRCPv2";
    case SdpMediaSection::kTcpTlsMrcpv2:
      return os << "TCP/TLS/MRCPv2";
    case SdpMediaSection::kPstn:
      return os << "PSTN";
    case SdpMediaSection::kUdpTlsUdptl:
      return os << "UDP/TLS/UDPTL";
    case SdpMediaSection::kSctp:
      return os << "SCTP";
    case SdpMediaSection::kDtlsSctp:
      return os << "DTLS/SCTP";
    case SdpMediaSection::kUdpDtlsSctp:
      return os << "UDP/DTLS/SCTP";
    case SdpMediaSection::kTcpDtlsSctp:
      return os << "TCP/DTLS/SCTP";
  }
  MOZ_ASSERT(false, "Unknown Protocol");
  return os << "?";
}

class SdpConnection {
 public:
  SdpConnection(const sdp::AddrType addrType, std::string addr,
                const uint8_t ttl = 0, const uint32_t count = 0)
      : mAddrType(addrType), mAddr(std::move(addr)), mTtl(ttl), mCount(count) {}
  ~SdpConnection() = default;

  sdp::AddrType GetAddrType() const { return mAddrType; }
  const std::string& GetAddress() const { return mAddr; }
  void SetAddress(const std::string& address) {
    mAddr = address;
    if (mAddr.find(':') != std::string::npos) {
      mAddrType = sdp::kIPv6;
    } else {
      mAddrType = sdp::kIPv4;
    }
  }
  uint8_t GetTtl() const { return mTtl; }
  uint32_t GetCount() const { return mCount; }

  void Serialize(std::ostream& os) const {
    sdp::NetType netType = sdp::kInternet;

    os << "c=" << netType << " " << mAddrType << " " << mAddr;

    if (mTtl) {
      os << "/" << static_cast<uint32_t>(mTtl);
      if (mCount) {
        os << "/" << mCount;
      }
    }
    os << "\r\n";
  }

 private:
  sdp::AddrType mAddrType;
  std::string mAddr;
  uint8_t mTtl;     // 0-255; 0 when unset
  uint32_t mCount;  // 0 when unset
};

inline std::ostream& operator<<(std::ostream& os, const SdpConnection& c) {
  c.Serialize(os);
  return os;
}

}  // namespace mozilla

#endif
