/*
 *  Copyright (c) 2012 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/video_coding/codecs/test/packet_manipulator.h"

#include <queue>

#include "gtest/gtest.h"
#include "modules/video_coding/codecs/interface/video_codec_interface.h"
#include "modules/video_coding/codecs/test/predictive_packet_manipulator.h"
#include "testsupport/unittest_utils.h"
#include "typedefs.h"

namespace webrtc {
namespace test {

const double kNeverDropProbability = 0.0;
const double kAlwaysDropProbability = 1.0;
const int kBurstLength = 1;

class PacketManipulatorTest: public PacketRelatedTest {
 protected:
  PacketReader packet_reader_;
  EncodedImage image_;
  NetworkingConfig drop_config_;
  NetworkingConfig no_drop_config_;

  PacketManipulatorTest() {
    image_._buffer = packet_data_;
    image_._length = kPacketDataLength;
    image_._size = kPacketDataLength;

    drop_config_.packet_size_in_bytes = kPacketSizeInBytes;
    drop_config_.packet_loss_probability = kAlwaysDropProbability;
    drop_config_.packet_loss_burst_length = kBurstLength;
    drop_config_.packet_loss_mode = kUniform;

    no_drop_config_.packet_size_in_bytes = kPacketSizeInBytes;
    no_drop_config_.packet_loss_probability = kNeverDropProbability;
    no_drop_config_.packet_loss_burst_length = kBurstLength;
    no_drop_config_.packet_loss_mode = kUniform;
  }

  virtual ~PacketManipulatorTest() {}

  void SetUp() {
    PacketRelatedTest::SetUp();
  }

  void TearDown() {
    PacketRelatedTest::TearDown();
  }

  void VerifyPacketLoss(int expected_nbr_packets_dropped,
                        int actual_nbr_packets_dropped,
                        int expected_packet_data_length,
                        WebRtc_UWord8* expected_packet_data,
                        EncodedImage& actual_image) {
    EXPECT_EQ(expected_nbr_packets_dropped, actual_nbr_packets_dropped);
    EXPECT_EQ(expected_packet_data_length, static_cast<int>(image_._length));
    EXPECT_EQ(0, memcmp(expected_packet_data, actual_image._buffer,
                        expected_packet_data_length));
  }
};

TEST_F(PacketManipulatorTest, Constructor) {
  PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_, false);
}

TEST_F(PacketManipulatorTest, DropNone) {
  PacketManipulatorImpl manipulator(&packet_reader_,  no_drop_config_, false);
  int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
  VerifyPacketLoss(0, nbr_packets_dropped, kPacketDataLength,
                   packet_data_, image_);
}

TEST_F(PacketManipulatorTest, UniformDropNoneSmallFrame) {
  int data_length = 400;  // smaller than the packet size
  image_._length = data_length;
  PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_, false);
  int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);

  VerifyPacketLoss(0, nbr_packets_dropped, data_length,
                     packet_data_, image_);
}

TEST_F(PacketManipulatorTest, UniformDropAll) {
  PacketManipulatorImpl manipulator(&packet_reader_, drop_config_, false);
  int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
  VerifyPacketLoss(kPacketDataNumberOfPackets, nbr_packets_dropped,
                   0, packet_data_, image_);
}

// Use our customized test class to make the second packet being lost
TEST_F(PacketManipulatorTest, UniformDropSinglePacket) {
  drop_config_.packet_loss_probability = 0.5;
  PredictivePacketManipulator manipulator(&packet_reader_, drop_config_);
  manipulator.AddRandomResult(1.0);
  manipulator.AddRandomResult(0.3);  // less than 0.5 will cause packet loss
  manipulator.AddRandomResult(1.0);

  // Execute the test target method:
  int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);

  // Since we setup the predictive packet manipulator, it will throw away the
  // second packet. The third packet is also lost because when we have lost one,
  // the remains shall also be discarded (in the current implementation).
  VerifyPacketLoss(2, nbr_packets_dropped, kPacketSizeInBytes, packet1_,
                   image_);
}

// Use our customized test class to make the second packet being lost
TEST_F(PacketManipulatorTest, BurstDropNinePackets) {
  // Create a longer packet data structure (10 packets)
  const int kNbrPackets = 10;
  const int kDataLength = kPacketSizeInBytes * kNbrPackets;
  WebRtc_UWord8 data[kDataLength];
  WebRtc_UWord8* data_pointer = data;
  // Fill with 0s, 1s and so on to be able to easily verify which were dropped:
  for (int i = 0; i < kNbrPackets; ++i) {
    memset(data_pointer + i * kPacketSizeInBytes, i, kPacketSizeInBytes);
  }
  // Overwrite the defaults from the test fixture:
  image_._buffer = data;
  image_._length = kDataLength;
  image_._size = kDataLength;

  drop_config_.packet_loss_probability = 0.5;
  drop_config_.packet_loss_burst_length = 5;
  drop_config_.packet_loss_mode = kBurst;
  PredictivePacketManipulator manipulator(&packet_reader_, drop_config_);
  manipulator.AddRandomResult(1.0);
  manipulator.AddRandomResult(0.3);  // less than 0.5 will cause packet loss
  for (int i = 0; i < kNbrPackets - 2; ++i) {
    manipulator.AddRandomResult(1.0);
  }

  // Execute the test target method:
  int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);

  // Should discard every packet after the first one.
  VerifyPacketLoss(9, nbr_packets_dropped, kPacketSizeInBytes, data, image_);
}

}  // namespace test
}  // namespace webrtc
