/*
 *  Copyright (c) 2016 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 <bitset>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>

#include "api/rtp_headers.h"
#include "api/transport/rtp/corruption_detection_message.h"
#include "api/video/color_space.h"
#include "api/video/video_content_type.h"
#include "api/video/video_layers_allocation.h"
#include "api/video/video_timing.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/corruption_detection_extension.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h"
#include "test/fuzzers/fuzz_data_helper.h"

namespace webrtc {
// We decide which header extensions to register by reading four bytes
// from the beginning of `data` and interpreting it as a bitmask over
// the RTPExtensionType enum. This assert ensures four bytes are enough.
static_assert(kRtpExtensionNumberOfExtensions <= 32,
              "Insufficient bits read to configure all header extensions. Add "
              "an extra byte and update the switches.");

void FuzzOneInput(FuzzDataHelper fuzz_data) {
  if (fuzz_data.size() <= 4)
    return;

  // Don't use the configuration byte as part of the packet.
  std::bitset<32> extensionMask(fuzz_data.Read<uint32_t>());

  RtpPacketReceived::ExtensionManager extensions(/*extmap_allow_mixed=*/true);
  // Start at local_id = 1 since 0 is an invalid extension id.
  int local_id = 1;
  // Skip i = 0 since it maps to kRtpExtensionNone.
  for (int i = 1; i < kRtpExtensionNumberOfExtensions; i++) {
    RTPExtensionType extension_type = static_cast<RTPExtensionType>(i);
    if (extensionMask[i]) {
      // Extensions are registered with an ID, which you signal to the
      // peer so they know what to expect. This code only cares about
      // parsing so the value of the ID isn't relevant.
      extensions.RegisterByType(local_id++, extension_type);
    }
  }

  RtpPacketReceived packet(&extensions);
  packet.Parse(fuzz_data.ReadRemaining());

  // Call packet accessors because they have extra checks.
  packet.Marker();
  packet.PayloadType();
  packet.SequenceNumber();
  packet.Timestamp();
  packet.Ssrc();
  packet.Csrcs();

  // Each extension has its own getter. It is supported behaviour to
  // call GetExtension on an extension which was not registered, so we
  // don't check the bitmask here.
  for (int i = 0; i < kRtpExtensionNumberOfExtensions; i++) {
    switch (static_cast<RTPExtensionType>(i)) {
      case kRtpExtensionNone:
      case kRtpExtensionNumberOfExtensions:
        break;
      case kRtpExtensionTransmissionTimeOffset: {
        int32_t offset;
        packet.GetExtension<TransmissionOffset>(&offset);
        break;
      }
      case kRtpExtensionAudioLevel: {
        AudioLevel audio_level;
        packet.GetExtension<AudioLevelExtension>(&audio_level);
        break;
      }
#if !defined(WEBRTC_MOZILLA_BUILD)
      case kRtpExtensionCsrcAudioLevel: {
        std::vector<uint8_t> audio_levels;
        packet.GetExtension<CsrcAudioLevel>(&audio_levels);
        break;
      }
#endif
      case kRtpExtensionAbsoluteSendTime:
        uint32_t sendtime;
        packet.GetExtension<AbsoluteSendTime>(&sendtime);
        break;
      case kRtpExtensionAbsoluteCaptureTime: {
        AbsoluteCaptureTime extension;
        packet.GetExtension<AbsoluteCaptureTimeExtension>(&extension);
        break;
      }
      case kRtpExtensionVideoRotation: {
        uint8_t rotation;
        packet.GetExtension<VideoOrientation>(&rotation);
        break;
      }
      case kRtpExtensionTransportSequenceNumber: {
        uint16_t seqnum;
        packet.GetExtension<TransportSequenceNumber>(&seqnum);
        break;
      }
      case kRtpExtensionTransportSequenceNumber02: {
        uint16_t seqnum;
        std::optional<FeedbackRequest> feedback_request;
        packet.GetExtension<TransportSequenceNumberV2>(&seqnum,
                                                       &feedback_request);
        break;
      }
      case kRtpExtensionPlayoutDelay: {
        VideoPlayoutDelay playout;
        packet.GetExtension<PlayoutDelayLimits>(&playout);
        break;
      }
      case kRtpExtensionVideoContentType: {
        VideoContentType content_type;
        packet.GetExtension<VideoContentTypeExtension>(&content_type);
        break;
      }
      case kRtpExtensionVideoTiming: {
        VideoSendTiming timing;
        packet.GetExtension<VideoTimingExtension>(&timing);
        break;
      }
      case kRtpExtensionRtpStreamId: {
        std::string rsid;
        packet.GetExtension<RtpStreamId>(&rsid);
        break;
      }
      case kRtpExtensionRepairedRtpStreamId: {
        std::string rsid;
        packet.GetExtension<RepairedRtpStreamId>(&rsid);
        break;
      }
      case kRtpExtensionMid: {
        std::string mid;
        packet.GetExtension<RtpMid>(&mid);
        break;
      }
      case kRtpExtensionGenericFrameDescriptor: {
        RtpGenericFrameDescriptor descriptor;
        packet.GetExtension<RtpGenericFrameDescriptorExtension00>(&descriptor);
        break;
      }
      case kRtpExtensionColorSpace: {
        ColorSpace color_space;
        packet.GetExtension<ColorSpaceExtension>(&color_space);
        break;
      }
      case kRtpExtensionInbandComfortNoise: {
        std::optional<uint8_t> noise_level;
        packet.GetExtension<InbandComfortNoiseExtension>(&noise_level);
        break;
      }
      case kRtpExtensionVideoLayersAllocation: {
        VideoLayersAllocation allocation;
        packet.GetExtension<RtpVideoLayersAllocationExtension>(&allocation);
        break;
      }
      case kRtpExtensionVideoFrameTrackingId: {
        uint16_t tracking_id;
        packet.GetExtension<VideoFrameTrackingIdExtension>(&tracking_id);
        break;
      }
      case kRtpExtensionDependencyDescriptor:
        // This extension requires state to read and so complicated that
        // deserves own fuzzer.
        break;
      case kRtpExtensionCorruptionDetection: {
        CorruptionDetectionMessage message;
        packet.GetExtension<CorruptionDetectionExtension>(&message);
        break;
#if defined(WEBRTC_MOZILLA_BUILD)
      case kRtpExtensionCsrcAudioLevel: {
        CsrcAudioLevelList levels;
        packet.GetExtension<CsrcAudioLevel>(&levels);
        break;
      }
#endif
    }
  }

  // Check that zero-ing mutable extensions wouldn't cause any problems.
  packet.ZeroMutableExtensions();
}
}  // namespace webrtc
