/*
 *  Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "modules/rtp_rtcp/source/flexfec_header_reader_writer.h"

#include <cstdint>
#include <cstring>
#include <span>
#include <vector>

#include "api/make_ref_counted.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {

namespace {

using Packet = ForwardErrorCorrection::Packet;
using ProtectedStream = ForwardErrorCorrection::ProtectedStream;
using ReceivedFecPacket = ForwardErrorCorrection::ReceivedFecPacket;
using ::testing::Each;
using ::testing::ElementsAreArray;

constexpr uint8_t kKBit = 1 << 7;
constexpr uint8_t kMask0[] = {0x2B, 0xCD};  // First K bit is cleared.
constexpr uint8_t kMask1[] = {0x92, 0x34,   // First K bit set.
                              0x76, 0x78, 0x9A, 0xBC};  // Second K bit cleared.
constexpr uint8_t kMask2[] = {0x92, 0x34,               //  First K bit set.
                              0xD6, 0x78, 0x9A, 0xBC,   // Second K bit set.
                              0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};

constexpr size_t kMediaPacketLength = 1234;

// Reader tests.
constexpr uint8_t kFlexible = 0b00 << 6;
constexpr uint8_t kPtRecovery = 123;
constexpr uint8_t kLengthRecovery[] = {0xab, 0xcd};
constexpr uint8_t kTsRecovery[] = {0x01, 0x23, 0x45, 0x67};
constexpr uint8_t kSnBases[4][2] = {{0x01, 0x02},
                                    {0x03, 0x04},
                                    {0x05, 0x06},
                                    {0x07, 0x08}};
constexpr uint8_t kPayloadBits = 0x00;

struct FecPacketStreamReadProperties {
  ProtectedStream stream;
  std::span<const uint8_t> mask;
};

struct FecPacketStreamWriteProperties {
  size_t byte_index;
  uint16_t seq_num_base;
  std::span<const uint8_t> mask;
};

Packet WritePacket(
    std::vector<FecHeaderWriter::ProtectedStream> protected_streams) {
  Packet written_packet;
  written_packet.data.SetSize(kMediaPacketLength);
  uint8_t* data = written_packet.data.MutableData();
  for (size_t i = 0; i < written_packet.data.size(); ++i) {
    data[i] = i;
  }

  FlexfecHeaderWriter writer;
  writer.FinalizeFecHeader(protected_streams, written_packet);
  return written_packet;
}

void VerifyReadHeaders(size_t expected_fec_header_size,
                       const ReceivedFecPacket& read_packet,
                       std::vector<FecPacketStreamReadProperties> expected) {
  EXPECT_EQ(read_packet.fec_header_size, expected_fec_header_size);
  const size_t protected_streams_num = read_packet.protected_streams.size();
  EXPECT_EQ(protected_streams_num, expected.size());
  for (size_t i = 0; i < protected_streams_num; ++i) {
    SCOPED_TRACE(i);
    ProtectedStream protected_stream = read_packet.protected_streams[i];
    EXPECT_EQ(protected_stream.ssrc, expected[i].stream.ssrc);
    EXPECT_EQ(protected_stream.seq_num_base, expected[i].stream.seq_num_base);
    EXPECT_EQ(protected_stream.packet_mask_offset,
              expected[i].stream.packet_mask_offset);
    EXPECT_EQ(protected_stream.packet_mask_size,
              expected[i].stream.packet_mask_size);
    // Ensure that the K-bits are removed and the packet mask has been packed.
    EXPECT_THAT(std::span(read_packet.pkt->data.cdata() +
                              protected_stream.packet_mask_offset,
                          protected_stream.packet_mask_size),
                ElementsAreArray(expected[i].mask));
  }
  EXPECT_EQ(read_packet.pkt->data.size() - expected_fec_header_size,
            read_packet.protection_length);
}

void VerifyFinalizedHeaders(
    const Packet& written_packet,
    std::vector<FecPacketStreamWriteProperties> expected) {
  const uint8_t* packet = written_packet.data.data();
  EXPECT_EQ(packet[0] & 0x80, 0x00);  // F bit clear.
  EXPECT_EQ(packet[0] & 0x40, 0x00);  // R bit clear.
  for (size_t i = 0; i < expected.size(); ++i) {
    SCOPED_TRACE(i);
    // Verify value of seq_num_base.
    EXPECT_EQ(
        ByteReader<uint16_t>::ReadBigEndian(packet + expected[i].byte_index),
        expected[i].seq_num_base);
    // Verify mask.
    EXPECT_THAT(
        std::span(packet + expected[i].byte_index + 2, expected[i].mask.size()),
        ElementsAreArray(expected[i].mask));
  }
}

void VerifyWrittenAndReadHeaders(
    std::vector<FecHeaderWriter::ProtectedStream> write_protected_streams,
    uint16_t expected_header_size) {
  // Write FEC Header.
  Packet written_packet = WritePacket(write_protected_streams);

  // Read FEC Header using written data.
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data = written_packet.data;
  for (const FecHeaderWriter::ProtectedStream& stream :
       write_protected_streams) {
    read_packet.protected_streams.push_back({.ssrc = stream.ssrc});
  }

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  // Verify header contents.
  EXPECT_EQ(read_packet.fec_header_size, expected_header_size);
  EXPECT_EQ(read_packet.protected_streams.size(),
            write_protected_streams.size());
  for (size_t i = 0; i < write_protected_streams.size(); ++i) {
    SCOPED_TRACE(i);
    EXPECT_EQ(read_packet.protected_streams[i].seq_num_base,
              write_protected_streams[i].seq_num_base);

    size_t mask_write_size = write_protected_streams[i].packet_mask.size();
    // Read mask returned may be larger than the mask that was sent to the
    // writer; That is ok as long as the specified part of the mask matches, and
    // the rest is 0s.
    FlexfecHeaderWriter writer;
    size_t expected_mask_read_size = writer.MinPacketMaskSize(
        write_protected_streams[i].packet_mask.data(), mask_write_size);
    EXPECT_EQ(read_packet.protected_streams[i].packet_mask_size,
              expected_mask_read_size);

    const uint8_t* read_mask_ptr =
        read_packet.pkt->data.cdata() +
        read_packet.protected_streams[i].packet_mask_offset;
    // Verify actual mask bits.
    EXPECT_THAT(std::span(read_mask_ptr, mask_write_size),
                ElementsAreArray(write_protected_streams[i].packet_mask));
    // If read mask size is larger than written mask size, verify all other bits
    // are 0.
    EXPECT_THAT(std::span(read_mask_ptr + mask_write_size,
                          expected_mask_read_size - mask_write_size),
                Each(0));
  }

  // Verify that the call to ReadFecHeader did not tamper with the payload.
  EXPECT_THAT(
      std::span(read_packet.pkt->data.cdata() + read_packet.fec_header_size,
                read_packet.pkt->data.size() - read_packet.fec_header_size),
      ElementsAreArray(written_packet.data.cdata() + expected_header_size,
                       written_packet.data.size() - expected_header_size));
}

}  // namespace

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0ClearSingleStream) {
  constexpr size_t kExpectedFecHeaderSize = 12;
  constexpr uint16_t kSnBase = 0x0102;
  constexpr uint8_t kFlexfecPktMask[] = {0x08, 0x81};
  constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3],
      kSnBase >> 8,   kSnBase & 0xFF, kFlexfecPktMask[0], kFlexfecPktMask[1],
      kPayloadBits,   kPayloadBits,   kPayloadBits,       kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask)},
       .mask = kUlpfecPacketMask}};

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1ClearSingleStream) {
  constexpr size_t kExpectedFecHeaderSize = 16;
  constexpr uint16_t kSnBase = 0x0102;
  constexpr uint8_t kFlexfecPktMask[] = {kKBit | 0x48, 0x81,  //
                                         0x02,         0x11, 0x00, 0x21};
  constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02,  //
                                           0x08, 0x44, 0x00, 0x84};
  constexpr uint8_t kPacketData[] = {
      kFlexible,          kPtRecovery,        kLengthRecovery[0],
      kLengthRecovery[1], kTsRecovery[0],     kTsRecovery[1],
      kTsRecovery[2],     kTsRecovery[3],     kSnBase >> 8,
      kSnBase & 0xFF,     kFlexfecPktMask[0], kFlexfecPktMask[1],
      kFlexfecPktMask[2], kFlexfecPktMask[3], kFlexfecPktMask[4],
      kFlexfecPktMask[5], kPayloadBits,       kPayloadBits,
      kPayloadBits,       kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask)},
       .mask = kUlpfecPacketMask}};

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithBothKBitsSetSingleStream) {
  constexpr size_t kExpectedFecHeaderSize = 24;
  constexpr uint16_t kSnBase = 0x0102;
  constexpr uint8_t kFlexfecPacketMask[] = {kKBit | 0x48, 0x81,              //
                                            kKBit | 0x02, 0x11, 0x00, 0x21,  //
                                            0x01,         0x11, 0x11, 0x11,
                                            0x11,         0x11, 0x11, 0x11};
  constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02,              //
                                           0x08, 0x44, 0x00, 0x84,  //
                                           0x04, 0x44, 0x44, 0x44,
                                           0x44, 0x44, 0x44, 0x44};
  constexpr uint8_t kPacketData[] = {kFlexible,
                                     kPtRecovery,
                                     kLengthRecovery[0],
                                     kLengthRecovery[1],
                                     kTsRecovery[0],
                                     kTsRecovery[1],
                                     kTsRecovery[2],
                                     kTsRecovery[3],
                                     kSnBase >> 8,
                                     kSnBase & 0xFF,
                                     kFlexfecPacketMask[0],
                                     kFlexfecPacketMask[1],
                                     kFlexfecPacketMask[2],
                                     kFlexfecPacketMask[3],
                                     kFlexfecPacketMask[4],
                                     kFlexfecPacketMask[5],
                                     kFlexfecPacketMask[6],
                                     kFlexfecPacketMask[7],
                                     kFlexfecPacketMask[8],
                                     kFlexfecPacketMask[9],
                                     kFlexfecPacketMask[10],
                                     kFlexfecPacketMask[11],
                                     kFlexfecPacketMask[12],
                                     kFlexfecPacketMask[13],
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask)},
       .mask = kUlpfecPacketMask}};

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0Clear2Streams) {
  constexpr size_t kExpectedFecHeaderSize = 16;
  constexpr uint16_t kSnBase0 = 0x0102;
  constexpr uint16_t kSnBase1 = 0x0304;
  constexpr uint8_t kFlexfecPktMask1[] = {0x08, 0x81};
  constexpr uint8_t kUlpfecPacketMask1[] = {0x11, 0x02};
  constexpr uint8_t kFlexfecPktMask2[] = {0x04, 0x41};
  constexpr uint8_t kUlpfecPacketMask2[] = {0x08, 0x82};

  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,     kLengthRecovery[0],  kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1],  kTsRecovery[2],      kTsRecovery[3],
      kSnBase0 >> 8,  kSnBase0 & 0xFF, kFlexfecPktMask1[0], kFlexfecPktMask1[1],
      kSnBase1 >> 8,  kSnBase1 & 0xFF, kFlexfecPktMask2[0], kFlexfecPktMask2[1],
      kPayloadBits,   kPayloadBits,    kPayloadBits,        kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase0,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask1)},
       .mask = kUlpfecPacketMask1},
      {.stream = {.ssrc = 0x02,
                  .seq_num_base = kSnBase1,
                  .packet_mask_offset = 14,
                  .packet_mask_size = std::size(kUlpfecPacketMask2)},
       .mask = kUlpfecPacketMask2},
  };

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1Clear2Streams) {
  constexpr size_t kExpectedFecHeaderSize = 24;
  constexpr uint16_t kSnBase0 = 0x0102;
  constexpr uint16_t kSnBase1 = 0x0304;
  constexpr uint8_t kFlexfecPktMask1[] = {kKBit | 0x48, 0x81,  //
                                          0x02,         0x11, 0x00, 0x21};
  constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02,  //
                                            0x08, 0x44, 0x00, 0x84};
  constexpr uint8_t kFlexfecPktMask2[] = {kKBit | 0x57, 0x82,  //
                                          0x04,         0x33, 0x00, 0x51};
  constexpr uint8_t kUlpfecPacketMask2[] = {0xAF, 0x04,  //
                                            0x10, 0xCC, 0x01, 0x44};
  constexpr uint8_t kPacketData[] = {
      kFlexible,           kPtRecovery,         kLengthRecovery[0],
      kLengthRecovery[1],  kTsRecovery[0],      kTsRecovery[1],
      kTsRecovery[2],      kTsRecovery[3],      kSnBase0 >> 8,
      kSnBase0 & 0xFF,     kFlexfecPktMask1[0], kFlexfecPktMask1[1],
      kFlexfecPktMask1[2], kFlexfecPktMask1[3], kFlexfecPktMask1[4],
      kFlexfecPktMask1[5], kSnBase1 >> 8,       kSnBase1 & 0xFF,
      kFlexfecPktMask2[0], kFlexfecPktMask2[1], kFlexfecPktMask2[2],
      kFlexfecPktMask2[3], kFlexfecPktMask2[4], kFlexfecPktMask2[5],
      kPayloadBits,        kPayloadBits,        kPayloadBits,
      kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase0,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask1)},
       .mask = kUlpfecPacketMask1},
      {.stream = {.ssrc = 0x02,
                  .seq_num_base = kSnBase1,
                  .packet_mask_offset = 18,
                  .packet_mask_size = std::size(kUlpfecPacketMask2)},
       .mask = kUlpfecPacketMask2},
  };

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithBothKBitsSet2Streams) {
  constexpr size_t kExpectedFecHeaderSize = 40;
  constexpr uint16_t kSnBase0 = 0x0102;
  constexpr uint16_t kSnBase1 = 0x0304;
  constexpr uint8_t kFlexfecPktMask1[] = {kKBit | 0x48, 0x81,              //
                                          kKBit | 0x02, 0x11, 0x00, 0x21,  //
                                          0x01,         0x11, 0x11, 0x11,
                                          0x11,         0x11, 0x11, 0x11};
  constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02,              //
                                            0x08, 0x44, 0x00, 0x84,  //
                                            0x04, 0x44, 0x44, 0x44,
                                            0x44, 0x44, 0x44, 0x44};
  constexpr uint8_t kFlexfecPktMask2[] = {kKBit | 0x32, 0x84,              //
                                          kKBit | 0x05, 0x23, 0x00, 0x55,  //
                                          0xA3,         0x22, 0x22, 0x22,
                                          0x22,         0x22, 0x22, 0x35};
  constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x08,              //
                                            0x14, 0x8C, 0x01, 0x56,  //
                                            0x8C, 0x88, 0x88, 0x88,
                                            0x88, 0x88, 0x88, 0xD4};

  constexpr uint8_t kPacketData[] = {kFlexible,
                                     kPtRecovery,
                                     kLengthRecovery[0],
                                     kLengthRecovery[1],
                                     kTsRecovery[0],
                                     kTsRecovery[1],
                                     kTsRecovery[2],
                                     kTsRecovery[3],
                                     kSnBase0 >> 8,
                                     kSnBase0 & 0xFF,
                                     kFlexfecPktMask1[0],
                                     kFlexfecPktMask1[1],
                                     kFlexfecPktMask1[2],
                                     kFlexfecPktMask1[3],
                                     kFlexfecPktMask1[4],
                                     kFlexfecPktMask1[5],
                                     kFlexfecPktMask1[6],
                                     kFlexfecPktMask1[7],
                                     kFlexfecPktMask1[8],
                                     kFlexfecPktMask1[9],
                                     kFlexfecPktMask1[10],
                                     kFlexfecPktMask1[11],
                                     kFlexfecPktMask1[12],
                                     kFlexfecPktMask1[13],
                                     kSnBase1 >> 8,
                                     kSnBase1 & 0xFF,
                                     kFlexfecPktMask2[0],
                                     kFlexfecPktMask2[1],
                                     kFlexfecPktMask2[2],
                                     kFlexfecPktMask2[3],
                                     kFlexfecPktMask2[4],
                                     kFlexfecPktMask2[5],
                                     kFlexfecPktMask2[6],
                                     kFlexfecPktMask2[7],
                                     kFlexfecPktMask2[8],
                                     kFlexfecPktMask2[9],
                                     kFlexfecPktMask2[10],
                                     kFlexfecPktMask2[11],
                                     kFlexfecPktMask2[12],
                                     kFlexfecPktMask2[13],
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase0,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask1)},
       .mask = kUlpfecPacketMask1},
      {.stream = {.ssrc = 0x02,
                  .seq_num_base = kSnBase1,
                  .packet_mask_offset = 26,
                  .packet_mask_size = std::size(kUlpfecPacketMask2)},
       .mask = kUlpfecPacketMask2},
  };

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadsHeaderWithMultipleStreamsMultipleMasks) {
  constexpr size_t kExpectedFecHeaderSize = 44;
  constexpr uint16_t kSnBase0 = 0x0102;
  constexpr uint16_t kSnBase1 = 0x0304;
  constexpr uint16_t kSnBase2 = 0x0506;
  constexpr uint16_t kSnBase3 = 0x0708;
  constexpr uint8_t kFlexfecPacketMask1[] = {0x29, 0x91};
  constexpr uint8_t kUlpfecPacketMask1[] = {0x53, 0x22};
  constexpr uint8_t kFlexfecPacketMask2[] = {kKBit | 0x32, 0xA1,  //
                                             0x02,         0x11, 0x00, 0x21};
  constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x42,  //
                                            0x08, 0x44, 0x00, 0x84};
  constexpr uint8_t kFlexfecPacketMask3[] = {kKBit | 0x48, 0x81,              //
                                             kKBit | 0x02, 0x11, 0x00, 0x21,  //
                                             0x01,         0x11, 0x11, 0x11,
                                             0x11,         0x11, 0x11, 0x11};
  constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02,              //
                                            0x08, 0x44, 0x00, 0x84,  //
                                            0x04, 0x44, 0x44, 0x44,
                                            0x44, 0x44, 0x44, 0x44};
  constexpr uint8_t kFlexfecPacketMask4[] = {kKBit | 0x32, 0x84,  //
                                             0x05,         0x23, 0x00, 0x55};
  constexpr uint8_t kUlpfecPacketMask4[] = {0x65, 0x08,  //
                                            0x14, 0x8C, 0x01, 0x54};
  constexpr uint8_t kPacketData[] = {kFlexible,
                                     kPtRecovery,
                                     kLengthRecovery[0],
                                     kLengthRecovery[1],
                                     kTsRecovery[0],
                                     kTsRecovery[1],
                                     kTsRecovery[2],
                                     kTsRecovery[3],
                                     kSnBase0 >> 8,
                                     kSnBase0 & 0xFF,
                                     kFlexfecPacketMask1[0],
                                     kFlexfecPacketMask1[1],
                                     kSnBase1 >> 8,
                                     kSnBase1 & 0xFF,
                                     kFlexfecPacketMask2[0],
                                     kFlexfecPacketMask2[1],
                                     kFlexfecPacketMask2[2],
                                     kFlexfecPacketMask2[3],
                                     kFlexfecPacketMask2[4],
                                     kFlexfecPacketMask2[5],
                                     kSnBase2 >> 8,
                                     kSnBase2 & 0xFF,
                                     kFlexfecPacketMask3[0],
                                     kFlexfecPacketMask3[1],
                                     kFlexfecPacketMask3[2],
                                     kFlexfecPacketMask3[3],
                                     kFlexfecPacketMask3[4],
                                     kFlexfecPacketMask3[5],
                                     kFlexfecPacketMask3[6],
                                     kFlexfecPacketMask3[7],
                                     kFlexfecPacketMask3[8],
                                     kFlexfecPacketMask3[9],
                                     kFlexfecPacketMask3[10],
                                     kFlexfecPacketMask3[11],
                                     kFlexfecPacketMask3[12],
                                     kFlexfecPacketMask3[13],
                                     kSnBase3 >> 8,
                                     kSnBase3 & 0xFF,
                                     kFlexfecPacketMask4[0],
                                     kFlexfecPacketMask4[1],
                                     kFlexfecPacketMask4[2],
                                     kFlexfecPacketMask4[3],
                                     kFlexfecPacketMask4[4],
                                     kFlexfecPacketMask4[5],
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits,
                                     kPayloadBits};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {
      {.ssrc = 0x01}, {.ssrc = 0x02}, {.ssrc = 0x03}, {.ssrc = 0x04}};

  FlexfecHeaderReader reader;
  EXPECT_TRUE(reader.ReadFecHeader(&read_packet));

  std::vector<FecPacketStreamReadProperties> expected = {
      {.stream = {.ssrc = 0x01,
                  .seq_num_base = kSnBase0,
                  .packet_mask_offset = 10,
                  .packet_mask_size = std::size(kUlpfecPacketMask1)},
       .mask = kUlpfecPacketMask1},
      {.stream = {.ssrc = 0x02,
                  .seq_num_base = kSnBase1,
                  .packet_mask_offset = 14,
                  .packet_mask_size = std::size(kUlpfecPacketMask2)},
       .mask = kUlpfecPacketMask2},
      {.stream = {.ssrc = 0x03,
                  .seq_num_base = kSnBase2,
                  .packet_mask_offset = 22,
                  .packet_mask_size = std::size(kUlpfecPacketMask3)},
       .mask = kUlpfecPacketMask3},
      {.stream = {.ssrc = 0x04,
                  .seq_num_base = kSnBase3,
                  .packet_mask_offset = 38,
                  .packet_mask_size = std::size(kUlpfecPacketMask4)},
       .mask = kUlpfecPacketMask4},
  };

  VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}

TEST(FlexfecHeaderReaderTest, ReadPacketWithoutProtectedSsrcsShouldFail) {
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  // No protected ssrcs.
  read_packet.protected_streams = {};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderReaderTest, ReadPacketWithoutStreamSpecificHeaderShouldFail) {
  // Simulate short received packet.
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  read_packet.pkt->data.SetData(kPacketData);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit0SetShouldFail) {
  // Simulate short received packet.
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3],
      kSnBases[0][0], kSnBases[0][1], kMask0[0],          kMask0[1]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  // Expected to have 2 bytes of mask but length of packet misses 1 byte.
  read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 1);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1ClearShouldFail) {
  // Simulate short received packet.
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3],
      kSnBases[0][0], kSnBases[0][1], kMask1[0],          kMask1[1],
      kMask1[2],      kMask1[3],      kMask1[4],          kMask1[5]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  // Expected to have 6 bytes of mask but length of packet misses 2 bytes.
  read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1SetShouldFail) {
  // Simulate short received packet.
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3],
      kSnBases[0][0], kSnBases[0][1], kMask2[0],          kMask2[1],
      kMask2[2],      kMask2[3],      kMask2[4],          kMask2[5],
      kMask2[6],      kMask2[7],      kMask2[8],          kMask2[9],
      kMask2[10],     kMask2[11],     kMask2[12],         kMask2[13]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  // Expected to have 14 bytes of mask but length of packet misses 2 bytes.
  read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2);
  read_packet.protected_streams = {{.ssrc = 0x01}};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderReaderTest, ReadShortPacketMultipleStreamsShouldFail) {
  // Simulate short received packet with 2 protected ssrcs.
  constexpr uint8_t kPacketData[] = {
      kFlexible,      kPtRecovery,    kLengthRecovery[0], kLengthRecovery[1],
      kTsRecovery[0], kTsRecovery[1], kTsRecovery[2],     kTsRecovery[3],
      kSnBases[0][0], kSnBases[0][1], kMask0[0],          kMask0[1],
      kSnBases[1][0], kSnBases[1][1], kMask2[0],          kMask2[1],
      kMask2[2],      kMask2[3],      kMask2[4],          kMask2[5],
      kMask2[6],      kMask2[7],      kMask2[8],          kMask2[9],
      kMask2[10],     kMask2[11],     kMask2[12],         kMask2[13]};
  ReceivedFecPacket read_packet;
  read_packet.pkt = make_ref_counted<Packet>();
  // Subtract 2 bytes from length, so the read will fail on parsing second
  read_packet.pkt->data.SetData(kPacketData, sizeof(kPacketData) - 2);
  read_packet.protected_streams = {{.ssrc = 0x01}, {.ssrc = 0x02}};

  FlexfecHeaderReader reader;
  EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}

TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit0ClearSingleStream) {
  constexpr uint8_t kFlexfecPacketMask[] = {0x08, 0x81};
  constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};
  constexpr uint16_t kMediaStartSeqNum = 1234;
  Packet written_packet = WritePacket({{.ssrc = 0x01,
                                        .seq_num_base = kMediaStartSeqNum,
                                        .packet_mask = kUlpfecPacketMask}});

  std::vector<FecPacketStreamWriteProperties> expected = {
      {.byte_index = 8,
       .seq_num_base = kMediaStartSeqNum,
       .mask = kFlexfecPacketMask}};

  VerifyFinalizedHeaders(written_packet, expected);
}

TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit1ClearSingleStream) {
  constexpr uint8_t kFlexfecPacketMask[] = {0xC8, 0x81, 0x02, 0x11, 0x00, 0x21};
  constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
  constexpr uint16_t kMediaStartSeqNum = 1234;
  Packet written_packet = WritePacket({{.ssrc = 0x01,
                                        .seq_num_base = kMediaStartSeqNum,
                                        .packet_mask = kUlpfecPacketMask}});

  std::vector<FecPacketStreamWriteProperties> expected = {
      {.byte_index = 8,
       .seq_num_base = kMediaStartSeqNum,
       .mask = kFlexfecPacketMask}};

  VerifyFinalizedHeaders(written_packet, expected);
}

TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithBothKBitsSetSingleStream) {
  constexpr uint8_t kFlexfecPacketMask[] = {
      0x91, 0x11,                                     // K-bit 0 set.
      0x91, 0x11, 0x11, 0x10,                         // K-bit 1 set.
      0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  //
  };
  constexpr uint8_t kUlpfecPacketMask[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
  constexpr uint16_t kMediaStartSeqNum = 1234;
  Packet written_packet = WritePacket({{.ssrc = 0x01,
                                        .seq_num_base = kMediaStartSeqNum,
                                        .packet_mask = kUlpfecPacketMask}});

  std::vector<FecPacketStreamWriteProperties> expected = {
      {.byte_index = 8,
       .seq_num_base = kMediaStartSeqNum,
       .mask = kFlexfecPacketMask}};

  VerifyFinalizedHeaders(written_packet, expected);
}

TEST(FlexfecHeaderWriterTest, FinalizesHeaderMultipleStreamsMultipleMasks) {
  constexpr uint8_t kFlexfecPacketMask1[] = {
      0x91, 0x11,                                     // K-bit 0 set.
      0x91, 0x11, 0x11, 0x10,                         // K-bit 1 set.
      0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  //
  };
  constexpr uint8_t kUlpfecPacketMask1[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
  constexpr uint16_t kMediaStartSeqNum1 = 1234;
  constexpr uint8_t kFlexfecPacketMask2[] = {0x08, 0x81};
  constexpr uint8_t kUlpfecPacketMask2[] = {0x11, 0x02};
  constexpr uint16_t kMediaStartSeqNum2 = 2345;
  constexpr uint8_t kFlexfecPacketMask3[] = {0xC8, 0x81, 0x02,
                                             0x11, 0x00, 0x21};
  constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
  constexpr uint16_t kMediaStartSeqNum3 = 3456;
  constexpr uint8_t kFlexfecPacketMask4[] = {
      0xD5, 0xAA,                                     // K-bit 0 set.
      0xA2, 0xAB, 0xCD, 0xEF,                         // K-bit 1 set.
      0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  //
  };
  constexpr uint8_t kUlpfecPacketMask4[] = {0xAB, 0x54, 0x8A, 0xAF, 0x37, 0xBF};
  constexpr uint16_t kMediaStartSeqNum4 = 4567;

  Packet written_packet = WritePacket({{.ssrc = 0x01,
                                        .seq_num_base = kMediaStartSeqNum1,
                                        .packet_mask = kUlpfecPacketMask1},
                                       {.ssrc = 0x02,
                                        .seq_num_base = kMediaStartSeqNum2,
                                        .packet_mask = kUlpfecPacketMask2},
                                       {.ssrc = 0x03,
                                        .seq_num_base = kMediaStartSeqNum3,
                                        .packet_mask = kUlpfecPacketMask3},
                                       {.ssrc = 0x04,
                                        .seq_num_base = kMediaStartSeqNum4,
                                        .packet_mask = kUlpfecPacketMask4}});

  std::vector<FecPacketStreamWriteProperties> expected = {
      {.byte_index = 8,
       .seq_num_base = kMediaStartSeqNum1,
       .mask = kFlexfecPacketMask1},
      {.byte_index = 24,
       .seq_num_base = kMediaStartSeqNum2,
       .mask = kFlexfecPacketMask2},
      {.byte_index = 28,
       .seq_num_base = kMediaStartSeqNum3,
       .mask = kFlexfecPacketMask3},
      {.byte_index = 36,
       .seq_num_base = kMediaStartSeqNum4,
       .mask = kFlexfecPacketMask4}};

  VerifyFinalizedHeaders(written_packet, expected);
}

// TODO(bugs.webrtc.org/15002): reimplement and add tests for multi stream cases
// after updating the MinPacketMaskSize and FecHeaderSize functions.

TEST(FlexfecHeaderWriterTest, ContractsShortUlpfecPacketMaskWithBit15Clear) {}

TEST(FlexfecHeaderWriterTest, ExpandsShortUlpfecPacketMaskWithBit15Set) {}

TEST(FlexfecHeaderWriterTest,
     ContractsLongUlpfecPacketMaskWithBit46ClearBit47Clear) {}

TEST(FlexfecHeaderWriterTest,
     ExpandsLongUlpfecPacketMaskWithBit46SetBit47Clear) {}

TEST(FlexfecHeaderWriterTest,
     ExpandsLongUlpfecPacketMaskWithBit46ClearBit47Set) {}

TEST(FlexfecHeaderWriterTest, ExpandsLongUlpfecPacketMaskWithBit46SetBit47Set) {
}

TEST(FlexfecHeaderReaderWriterTest,
     WriteAndReadSmallUlpfecPacketHeaderWithMaskBit15ClearSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};  // Bit 15 clear.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 12;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(FlexfecHeaderReaderWriterTest,
     WriteAndReadSmallUlpfecPacketHeaderWithMaskBit15SetSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {0xAA, 0xFF};  // Bit 15 set.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 16;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(FlexfecHeaderReaderWriterTest,
     WriteAndReadLargeUlpfecPacketHeaderWithMaskBits46And47ClearSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44,
                                           0x00, 0x84};  // Bits 46, 47 clear.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 16;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(
    FlexfecHeaderReaderWriterTest,
    WriteAndReadLargeUlpfecPacketHeaderWithMaskBit46SetBit47ClearSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {
      0x91, 0x02, 0x08, 0x44, 0x00, 0x86};  // Bit 46 set, bit 47 clear.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 24;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(
    FlexfecHeaderReaderWriterTest,
    WriteAndReadLargeUlpfecPacketHeaderMaskWithBit46ClearBit47SetSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {
      0x91, 0x02, 0x08, 0x44, 0x00, 0x85};  // Bit 46 clear, bit 47 set.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 24;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(FlexfecHeaderReaderWriterTest,
     WriteAndReadLargeUlpfecPacketHeaderWithMaskBits46And47SetSingleStream) {
  constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44,
                                           0x00, 0x87};  // Bits 46, 47 set.
  constexpr uint16_t kMediaStartSeqNum = 1234;
  constexpr uint16_t kExpectedHeaderSize = 24;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum,
                                .packet_mask = kUlpfecPacketMask}},
                              kExpectedHeaderSize);
}

TEST(FlexfecHeaderReaderWriterTest, WriteAndReadMultipleStreamsMultipleMasks) {
  constexpr uint8_t kUlpfecPacketMask1[] = {0x11, 0x02};
  constexpr uint16_t kMediaStartSeqNum1 = 1234;
  constexpr uint8_t kUlpfecPacketMask2[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
  constexpr uint16_t kMediaStartSeqNum2 = 2345;
  constexpr uint8_t kUlpfecPacketMask3[] = {0xAA, 0xFF};
  constexpr uint16_t kMediaStartSeqNum3 = 3456;
  constexpr uint8_t kUlpfecPacketMask4[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x87};
  constexpr uint16_t kMediaStartSeqNum4 = 4567;
  constexpr uint16_t kExpectedHeaderSize = 44;

  VerifyWrittenAndReadHeaders({{.ssrc = 0x01,
                                .seq_num_base = kMediaStartSeqNum1,
                                .packet_mask = kUlpfecPacketMask1},
                               {.ssrc = 0x02,
                                .seq_num_base = kMediaStartSeqNum2,
                                .packet_mask = kUlpfecPacketMask2},
                               {.ssrc = 0x03,
                                .seq_num_base = kMediaStartSeqNum3,
                                .packet_mask = kUlpfecPacketMask3},
                               {.ssrc = 0x04,
                                .seq_num_base = kMediaStartSeqNum4,
                                .packet_mask = kUlpfecPacketMask4}},
                              kExpectedHeaderSize);
}

}  // namespace webrtc
