// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_
#define MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_

#include <stddef.h>
#include <stdint.h>

#include "base/big_endian.h"
#include "base/macros.h"
#include "media/cast/logging/logging_defines.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/rtcp/rtcp_defines.h"

namespace media {
namespace cast {

// RFC 3550 page 44, including end null.
static const size_t kRtcpCnameSize = 256;

static const uint32_t kCast = ('C' << 24) + ('A' << 16) + ('S' << 8) + 'T';

static const uint32_t kCst2 = ('C' << 24) + ('S' << 16) + ('T' << 8) + '2';

static const uint8_t kReceiverLogSubtype = 2;

static const size_t kRtcpMaxReceiverLogMessages = 256;
static const size_t kRtcpMaxCastLossFields = 100;

struct RtcpCommonHeader {
  uint8_t V;   // Version.
  bool P;    // Padding.
  uint8_t IC;  // Item count / subtype.
  uint8_t PT;  // Packet Type.
  size_t length_in_octets;
};

class RtcpParser {
 public:
  RtcpParser(uint32_t local_ssrc, uint32_t remote_ssrc);

  RtcpParser(const RtcpParser&) = delete;
  RtcpParser& operator=(const RtcpParser&) = delete;

  ~RtcpParser();

  // Gets/Sets the ID of the latest frame that could possibly be ACK'ed.  This
  // is used when expanding truncated frame IDs during Parse().  This only needs
  // to be called if the client uses cast_message().
  FrameId max_valid_frame_id() const { return max_valid_frame_id_; }
  void SetMaxValidFrameId(FrameId max_valid_frame_id);

  // Parse the RTCP packet.
  bool Parse(base::BigEndianReader* reader);

  bool has_sender_report() const { return has_sender_report_; }
  const RtcpSenderInfo& sender_report() const {
    return sender_report_;
  }

  bool has_last_report() const { return has_last_report_; }
  uint32_t last_report() const { return last_report_; }
  uint32_t delay_since_last_report() const { return delay_since_last_report_; }

  bool has_receiver_log() const { return !receiver_log_.empty(); }
  const RtcpReceiverLogMessage& receiver_log() const { return receiver_log_; }
  RtcpReceiverLogMessage* mutable_receiver_log() { return & receiver_log_; }

  bool has_cast_message() const { return has_cast_message_; }
  const RtcpCastMessage& cast_message() const { return cast_message_; }
  RtcpCastMessage* mutable_cast_message() { return &cast_message_; }
  // Return if successfully parsed the extended feedback.
  bool has_cst2_message() const { return has_cst2_message_; }

  bool has_receiver_reference_time_report() const {
    return has_receiver_reference_time_report_;
  }
  const RtcpReceiverReferenceTimeReport&
  receiver_reference_time_report() const {
    return receiver_reference_time_report_;
  }

  bool has_picture_loss_indicator() const {
    return has_picture_loss_indicator_;
  }

 private:
  bool ParseCommonHeader(base::BigEndianReader* reader,
                         RtcpCommonHeader* parsed_header);
  bool ParseSR(base::BigEndianReader* reader, const RtcpCommonHeader& header);
  bool ParseRR(base::BigEndianReader* reader, const RtcpCommonHeader& header);
  bool ParseReportBlock(base::BigEndianReader* reader);
  bool ParsePli(base::BigEndianReader* reader, const RtcpCommonHeader& header);
  bool ParseApplicationDefined(base::BigEndianReader* reader,
                               const RtcpCommonHeader& header);
  bool ParseCastReceiverLogFrameItem(base::BigEndianReader* reader);
  bool ParseFeedbackCommon(base::BigEndianReader* reader,
                           const RtcpCommonHeader& header);
  bool ParseExtendedReport(base::BigEndianReader* reader,
                           const RtcpCommonHeader& header);
  bool ParseExtendedReportReceiverReferenceTimeReport(
      base::BigEndianReader* reader,
      uint32_t remote_ssrc);
  bool ParseExtendedReportDelaySinceLastReceiverReport(
      base::BigEndianReader* reader);

  const uint32_t local_ssrc_;
  const uint32_t remote_ssrc_;

  bool has_sender_report_;
  RtcpSenderInfo sender_report_;

  uint32_t last_report_;
  uint32_t delay_since_last_report_;
  bool has_last_report_;

  // |receiver_log_| is a vector vector, no need for has_*.
  RtcpReceiverLogMessage receiver_log_;

  bool has_cast_message_;
  RtcpCastMessage cast_message_;
  bool has_cst2_message_;

  bool has_receiver_reference_time_report_;
  RtcpReceiverReferenceTimeReport receiver_reference_time_report_;

  // Tracks recently-parsed RTP timestamps so that the truncated values can be
  // re-expanded into full-form.
  RtpTimeTicks last_parsed_sr_rtp_timestamp_;
  RtpTimeTicks last_parsed_frame_log_rtp_timestamp_;

  // The maximum possible re-expanded frame ID value.
  FrameId max_valid_frame_id_;

  // Indicates if sender received the Pli message from the receiver.
  bool has_picture_loss_indicator_;
};

// Converts a log event type to an integer value.
// NOTE: We have only allocated 4 bits to represent the type of event over the
// wire. Therefore, this function can only return values from 0 to 15.
uint8_t ConvertEventTypeToWireFormat(CastLoggingEvent event);

// The inverse of |ConvertEventTypeToWireFormat()|.
CastLoggingEvent TranslateToLogEventFromWireFormat(uint8_t event);

// Splits an NTP timestamp having a microsecond timebase into the standard two
// 32-bit integer wire format.
void ConvertTimeToFractions(int64_t ntp_time_us,
                            uint32_t* seconds,
                            uint32_t* fractions);

// Maps a base::TimeTicks value to an NTP timestamp comprised of two components.
void ConvertTimeTicksToNtp(const base::TimeTicks& time,
                           uint32_t* ntp_seconds,
                           uint32_t* ntp_fractions);

// Create a NTP diff from seconds and fractions of seconds; delay_fraction is
// fractions of a second where 0x80000000 is half a second.
uint32_t ConvertToNtpDiff(uint32_t delay_seconds, uint32_t delay_fraction);

// Maps an NTP timestamp, comprised of two components, to a base::TimeTicks
// value.
base::TimeTicks ConvertNtpToTimeTicks(uint32_t ntp_seconds,
                                      uint32_t ntp_fractions);

bool IsRtcpPacket(const uint8_t* packet, size_t length);

uint32_t GetSsrcOfSender(const uint8_t* rtcp_buffer, size_t length);

}  // namespace cast
}  // namespace media

#endif  // MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_
