/*
 *  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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"

namespace webrtc {
namespace {
const uint32_t kSenderSsrc = 0x12345;
const uint32_t kReceiverSsrc = 0x23456;
const uint32_t kOneWayNetworkDelayMs = 100;

class RtcpRttStatsTestImpl : public RtcpRttStats {
 public:
  RtcpRttStatsTestImpl() : rtt_ms_(0) {}
  virtual ~RtcpRttStatsTestImpl() {}

  virtual void OnRttUpdate(uint32_t rtt_ms) {
    rtt_ms_ = rtt_ms;
  }
  virtual uint32_t LastProcessedRtt() const {
    return rtt_ms_;
  }
  uint32_t rtt_ms_;
};

class SendTransport : public Transport,
                      public NullRtpData {
 public:
  SendTransport() : receiver_(NULL), clock_(NULL), delay_ms_(0) {}

  void SetRtpRtcpModule(ModuleRtpRtcpImpl* receiver) {
    receiver_ = receiver;
  }
  void SimulateNetworkDelay(uint32_t delay_ms, SimulatedClock* clock) {
    clock_ = clock;
    delay_ms_ = delay_ms;
  }
  virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) {
    return -1;
  }
  virtual int SendRTCPPacket(int /*ch*/, const void *data, int len) {
    if (clock_) {
      clock_->AdvanceTimeMilliseconds(delay_ms_);
    }
    EXPECT_TRUE(receiver_ != NULL);
    EXPECT_EQ(0, receiver_->IncomingRtcpPacket(
        static_cast<const uint8_t*>(data), len));
    return len;
  }
  ModuleRtpRtcpImpl* receiver_;
  SimulatedClock* clock_;
  uint32_t delay_ms_;
};

class RtpRtcpModule {
 public:
  RtpRtcpModule(SimulatedClock* clock)
      : receive_statistics_(ReceiveStatistics::Create(clock)) {
    RtpRtcp::Configuration config;
    config.audio = false;
    config.clock = clock;
    config.outgoing_transport = &transport_;
    config.receive_statistics = receive_statistics_.get();
    config.rtt_stats = &rtt_stats_;

    impl_.reset(new ModuleRtpRtcpImpl(config));
    EXPECT_EQ(0, impl_->SetRTCPStatus(kRtcpCompound));

    transport_.SimulateNetworkDelay(kOneWayNetworkDelayMs, clock);
  }

  RtcpPacketTypeCounter packets_sent_;
  RtcpPacketTypeCounter packets_received_;
  scoped_ptr<ReceiveStatistics> receive_statistics_;
  SendTransport transport_;
  RtcpRttStatsTestImpl rtt_stats_;
  scoped_ptr<ModuleRtpRtcpImpl> impl_;

  RtcpPacketTypeCounter RtcpSent() {
    impl_->GetRtcpPacketTypeCounters(&packets_sent_, &packets_received_);
    return packets_sent_;
  }
  RtcpPacketTypeCounter RtcpReceived() {
    impl_->GetRtcpPacketTypeCounters(&packets_sent_, &packets_received_);
    return packets_received_;
  }
};
}  // namespace

class RtpRtcpImplTest : public ::testing::Test {
 protected:
  RtpRtcpImplTest()
      : clock_(1335900000),
        sender_(&clock_),
        receiver_(&clock_) {
    // Send module.
    EXPECT_EQ(0, sender_.impl_->SetSendingStatus(true));
    EXPECT_EQ(0, sender_.impl_->SetSSRC(kSenderSsrc));
    sender_.impl_->SetRemoteSSRC(kReceiverSsrc);
    // Receive module.
    EXPECT_EQ(0, receiver_.impl_->SetSendingStatus(false));
    EXPECT_EQ(0, receiver_.impl_->SetSSRC(kReceiverSsrc));
    receiver_.impl_->SetRemoteSSRC(kSenderSsrc);
    // Transport settings.
    sender_.transport_.SetRtpRtcpModule(receiver_.impl_.get());
    receiver_.transport_.SetRtpRtcpModule(sender_.impl_.get());
  }
  SimulatedClock clock_;
  RtpRtcpModule sender_;
  RtpRtcpModule receiver_;
};

TEST_F(RtpRtcpImplTest, Rtt) {
  RTPHeader header = {};
  header.timestamp = 1;
  header.sequenceNumber = 123;
  header.ssrc = kSenderSsrc;
  header.headerLength = 12;
  receiver_.receive_statistics_->IncomingPacket(header, 100, false);

  // Sender module should send a SR.
  EXPECT_EQ(0, sender_.impl_->SendRTCP(kRtcpReport));

  // Receiver module should send a RR with a response to the last received SR.
  clock_.AdvanceTimeMilliseconds(1000);
  EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpReport));

  // Verify RTT.
  uint16_t rtt;
  uint16_t avg_rtt;
  uint16_t min_rtt;
  uint16_t max_rtt;
  EXPECT_EQ(0,
      sender_.impl_->RTT(kReceiverSsrc, &rtt, &avg_rtt, &min_rtt, &max_rtt));
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, rtt);
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, avg_rtt);
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, min_rtt);
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, max_rtt);

  // No RTT from other ssrc.
  EXPECT_EQ(-1,
      sender_.impl_->RTT(kReceiverSsrc+1, &rtt, &avg_rtt, &min_rtt, &max_rtt));

  // Verify RTT from rtt_stats config.
  EXPECT_EQ(0U, sender_.rtt_stats_.LastProcessedRtt());
  EXPECT_EQ(0U, sender_.impl_->rtt_ms());
  sender_.impl_->Process();
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, sender_.rtt_stats_.LastProcessedRtt());
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, sender_.impl_->rtt_ms());
}

TEST_F(RtpRtcpImplTest, SetRtcpXrRrtrStatus) {
  EXPECT_FALSE(receiver_.impl_->RtcpXrRrtrStatus());
  receiver_.impl_->SetRtcpXrRrtrStatus(true);
  EXPECT_TRUE(receiver_.impl_->RtcpXrRrtrStatus());
}

TEST_F(RtpRtcpImplTest, RttForReceiverOnly) {
  receiver_.impl_->SetRtcpXrRrtrStatus(true);

  // Receiver module should send a Receiver time reference report (RTRR).
  EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpReport));

  // Sender module should send a response to the last received RTRR (DLRR).
  clock_.AdvanceTimeMilliseconds(1000);
  EXPECT_EQ(0, sender_.impl_->SendRTCP(kRtcpReport));

  // Verify RTT.
  EXPECT_EQ(0U, receiver_.rtt_stats_.LastProcessedRtt());
  EXPECT_EQ(0U, receiver_.impl_->rtt_ms());
  receiver_.impl_->Process();
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.rtt_stats_.LastProcessedRtt());
  EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.impl_->rtt_ms());
}

TEST_F(RtpRtcpImplTest, RtcpPacketTypeCounter_Nack) {
  EXPECT_EQ(0U, sender_.RtcpReceived().nack_packets);
  EXPECT_EQ(0U, receiver_.RtcpSent().nack_packets);
  // Receive module sends a NACK.
  const uint16_t kNackLength = 1;
  uint16_t nack_list[kNackLength] = {123};
  EXPECT_EQ(0, receiver_.impl_->SendNACK(nack_list, kNackLength));
  EXPECT_EQ(1U, receiver_.RtcpSent().nack_packets);

  // Send module receives the NACK.
  EXPECT_EQ(1U, sender_.RtcpReceived().nack_packets);
}

TEST_F(RtpRtcpImplTest, RtcpPacketTypeCounter_FirAndPli) {
  EXPECT_EQ(0U, sender_.RtcpReceived().fir_packets);
  EXPECT_EQ(0U, receiver_.RtcpSent().fir_packets);
  // Receive module sends a FIR.
  EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpFir));
  EXPECT_EQ(1U, receiver_.RtcpSent().fir_packets);
  // Send module receives the FIR.
  EXPECT_EQ(1U, sender_.RtcpReceived().fir_packets);

  // Receive module sends a FIR and PLI.
  EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpFir | kRtcpPli));
  EXPECT_EQ(2U, receiver_.RtcpSent().fir_packets);
  EXPECT_EQ(1U, receiver_.RtcpSent().pli_packets);
  // Send module receives the FIR and PLI.
  EXPECT_EQ(2U, sender_.RtcpReceived().fir_packets);
  EXPECT_EQ(1U, sender_.RtcpReceived().pli_packets);
}
}  // namespace webrtc
