/*  Copyright (c) 2013 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 <string.h>

#include <list>

#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/video_coding/main/source/packet.h"
#include "webrtc/modules/video_coding/main/source/receiver.h"
#include "webrtc/modules/video_coding/main/source/test/stream_generator.h"
#include "webrtc/modules/video_coding/main/source/timing.h"
#include "webrtc/modules/video_coding/main/test/test_util.h"
#include "webrtc/system_wrappers/interface/clock.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"

namespace webrtc {

class TestVCMReceiver : public ::testing::Test {
 protected:
  enum { kDataBufferSize = 10 };
  enum { kWidth = 640 };
  enum { kHeight = 480 };

  TestVCMReceiver()
      : clock_(new SimulatedClock(0)),
        timing_(clock_.get()),
        receiver_(&timing_, clock_.get(), &event_factory_, true) {
    stream_generator_.reset(new
        StreamGenerator(0, 0, clock_->TimeInMilliseconds()));
    memset(data_buffer_, 0, kDataBufferSize);
  }

  virtual void SetUp() {
    receiver_.Reset();
  }

  int32_t InsertPacket(int index) {
    VCMPacket packet;
    packet.dataPtr = data_buffer_;
    bool packet_available = stream_generator_->GetPacket(&packet, index);
    EXPECT_TRUE(packet_available);
    if (!packet_available)
      return kGeneralError;  // Return here to avoid crashes below.
    // Arbitrary width and height.
    return receiver_.InsertPacket(packet, 640, 480);
  }

  int32_t InsertPacketAndPop(int index) {
    VCMPacket packet;
    packet.dataPtr = data_buffer_;
    bool packet_available = stream_generator_->PopPacket(&packet, index);
    EXPECT_TRUE(packet_available);
    if (!packet_available)
      return kGeneralError;  // Return here to avoid crashes below.
    return receiver_.InsertPacket(packet, kWidth, kHeight);
  }

  int32_t InsertFrame(FrameType frame_type, bool complete) {
    int num_of_packets = complete ? 1 : 2;
    stream_generator_->GenerateFrame(
        frame_type,
        (frame_type != kFrameEmpty) ? num_of_packets : 0,
        (frame_type == kFrameEmpty) ? 1 : 0,
        clock_->TimeInMilliseconds());
    int32_t ret = InsertPacketAndPop(0);
    if (!complete) {
      // Drop the second packet.
      VCMPacket packet;
      stream_generator_->PopPacket(&packet, 0);
    }
    clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs);
    return ret;
  }

  bool DecodeNextFrame() {
    int64_t render_time_ms = 0;
    VCMEncodedFrame* frame = receiver_.FrameForDecoding(0, render_time_ms,
                                                        false, NULL);
    if (!frame)
      return false;
    receiver_.ReleaseFrame(frame);
    return true;
  }

  scoped_ptr<SimulatedClock> clock_;
  VCMTiming timing_;
  NullEventFactory event_factory_;
  VCMReceiver receiver_;
  scoped_ptr<StreamGenerator> stream_generator_;
  uint8_t data_buffer_[kDataBufferSize];
};

TEST_F(TestVCMReceiver, RenderBufferSize_AllComplete) {
  EXPECT_EQ(0, receiver_.RenderBufferSizeMs());
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  int num_of_frames = 10;
  for (int i = 0; i < num_of_frames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  EXPECT_EQ(num_of_frames * kDefaultFramePeriodMs,
            receiver_.RenderBufferSizeMs());
}

TEST_F(TestVCMReceiver, RenderBufferSize_SkipToKeyFrame) {
  EXPECT_EQ(0, receiver_.RenderBufferSizeMs());
  const int kNumOfNonDecodableFrames = 2;
  for (int i = 0; i < kNumOfNonDecodableFrames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  const int kNumOfFrames = 10;
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  for (int i = 0; i < kNumOfFrames - 1; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  EXPECT_EQ((kNumOfFrames - 1) * kDefaultFramePeriodMs,
      receiver_.RenderBufferSizeMs());
}

TEST_F(TestVCMReceiver, RenderBufferSize_NotAllComplete) {
  EXPECT_EQ(0, receiver_.RenderBufferSizeMs());
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  int num_of_frames = 10;
  for (int i = 0; i < num_of_frames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  num_of_frames++;
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  for (int i = 0; i < num_of_frames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  EXPECT_EQ((num_of_frames - 1) * kDefaultFramePeriodMs,
      receiver_.RenderBufferSizeMs());
}

TEST_F(TestVCMReceiver, RenderBufferSize_NoKeyFrame) {
  EXPECT_EQ(0, receiver_.RenderBufferSizeMs());
  int num_of_frames = 10;
  for (int i = 0; i < num_of_frames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  int64_t next_render_time_ms = 0;
  VCMEncodedFrame* frame = receiver_.FrameForDecoding(10, next_render_time_ms);
  EXPECT_TRUE(frame == NULL);
  receiver_.ReleaseFrame(frame);
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  for (int i = 0; i < num_of_frames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  EXPECT_EQ(0, receiver_.RenderBufferSizeMs());
}

TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  const int kMinDelayMs = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Advance time until it's time to decode the key frame.
  clock_->AdvanceTimeMilliseconds(kMinDelayMs);
  EXPECT_TRUE(DecodeNextFrame());
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackOk, ret);
}

TEST_F(TestVCMReceiver, NonDecodableDuration_NoKeyFrame) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  const int kNumFrames = kDefaultFrameRate * kMaxNonDecodableDuration / 1000;
  for (int i = 0; i < kNumFrames; ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackKeyFrameRequest, ret);
}

TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  const int kMaxNonDecodableDurationFrames = (kDefaultFrameRate *
      kMaxNonDecodableDuration + 500) / 1000;
  const int kMinDelayMs = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  receiver_.SetMinReceiverDelay(kMinDelayMs);
  int64_t key_frame_inserted = clock_->TimeInMilliseconds();
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Insert an incomplete frame.
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  // Insert enough frames to have too long non-decodable sequence.
  for (int i = 0; i < kMaxNonDecodableDurationFrames;
       ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  // Advance time until it's time to decode the key frame.
  clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() -
      key_frame_inserted);
  EXPECT_TRUE(DecodeNextFrame());
  // Make sure we get a key frame request.
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackKeyFrameRequest, ret);
}

TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  const int kMaxNonDecodableDurationFrames = (kDefaultFrameRate *
      kMaxNonDecodableDuration + 500) / 1000;
  const int kMinDelayMs = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  receiver_.SetMinReceiverDelay(kMinDelayMs);
  int64_t key_frame_inserted = clock_->TimeInMilliseconds();
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Insert an incomplete frame.
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  // Insert all but one frame to not trigger a key frame request due to
  // too long duration of non-decodable frames.
  for (int i = 0; i < kMaxNonDecodableDurationFrames - 1;
       ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  // Advance time until it's time to decode the key frame.
  clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() -
      key_frame_inserted);
  EXPECT_TRUE(DecodeNextFrame());
  // Make sure we don't get a key frame request since we haven't generated
  // enough frames.
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackOk, ret);
}

TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  const int kMaxNonDecodableDurationFrames = (kDefaultFrameRate *
      kMaxNonDecodableDuration + 500) / 1000;
  const int kMinDelayMs = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  receiver_.SetMinReceiverDelay(kMinDelayMs);
  int64_t key_frame_inserted = clock_->TimeInMilliseconds();
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Insert enough frames to have too long non-decodable sequence, except that
  // we don't have any losses.
  for (int i = 0; i < kMaxNonDecodableDurationFrames;
       ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  // Insert an incomplete frame.
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  // Advance time until it's time to decode the key frame.
  clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() -
      key_frame_inserted);
  EXPECT_TRUE(DecodeNextFrame());
  // Make sure we don't get a key frame request since the non-decodable duration
  // is only one frame.
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackOk, ret);
}

TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) {
  // Enable NACK and with no RTT thresholds for disabling retransmission delay.
  receiver_.SetNackMode(kNack, -1, -1);
  const size_t kMaxNackListSize = 1000;
  const int kMaxPacketAgeToNack = 1000;
  const int kMaxNonDecodableDuration = 500;
  const int kMaxNonDecodableDurationFrames = (kDefaultFrameRate *
      kMaxNonDecodableDuration + 500) / 1000;
  const int kMinDelayMs = 500;
  receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
      kMaxNonDecodableDuration);
  receiver_.SetMinReceiverDelay(kMinDelayMs);
  int64_t key_frame_inserted = clock_->TimeInMilliseconds();
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Insert an incomplete frame.
  EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError);
  // Insert enough frames to have too long non-decodable sequence.
  for (int i = 0; i < kMaxNonDecodableDurationFrames;
       ++i) {
    EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError);
  }
  EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError);
  // Advance time until it's time to decode the key frame.
  clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() -
      key_frame_inserted);
  EXPECT_TRUE(DecodeNextFrame());
  // Make sure we don't get a key frame request since we have a key frame
  // in the list.
  uint16_t nack_list[kMaxNackListSize];
  uint16_t nack_list_length = 0;
  VCMNackStatus ret = receiver_.NackList(nack_list, kMaxNackListSize,
                                         &nack_list_length);
  EXPECT_EQ(kNackOk, ret);
}
}  // namespace webrtc
