/*
 *  Copyright (c) 2011 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 <cassert>
#include <windows.h>
#include <iostream>
#include <tchar.h>

#include "rtp_rtcp.h"
#include "common_types.h"
#include "rtcp_utility.h"
#include "tmmbr_help.h"

#define TEST_STR "Test TMMBR."
#define TEST_PASSED() std::cerr << TEST_STR << " : [OK]" << std::endl
#define PRINT_LINE std::cout << "------------------------------------------" << std::endl;


const int maxFileLen = 200;
WebRtc_UWord8* dataFile[maxFileLen];


struct InputSet
{
    WebRtc_UWord32 TMMBR;
    WebRtc_UWord32 packetOH;
    WebRtc_UWord32 SSRC;
};

const InputSet set0   = {220,  80, 11111};  // bitRate, packetOH, ssrc
const InputSet set1   = {180,  90, 22222};
const InputSet set2   = {100, 210, 33333};
const InputSet set3   = { 35,  40, 44444};
const InputSet set4   = { 40,  60, 55555};
const InputSet set4_1 = {100,  60, 55555};
const InputSet set4_2 = { 10,  60, 55555};
const InputSet set5   = {200,  40, 66666};
const InputSet set00  = {  0,  40, 66666};

const int maxBitrate = 230;  // if this is lower than max in the list above test should fail

void Verify(TMMBRSet* boundingSet, int index, InputSet set)
{
    assert(boundingSet->ptrTmmbrSet[index]    == set.TMMBR);
    assert(boundingSet->ptrPacketOHSet[index] == set.packetOH);
    assert(boundingSet->ptrSsrcSet[index]     == set.SSRC);
};

int ParseRTCPPacket(const void *data, int len, TMMBRSet*& boundingSet)
{
    int numItems = -1;
    RTCPUtility::RTCPParserV2 rtcpParser((const WebRtc_UWord8*)data, len, true);
    RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Begin();
    while (pktType != RTCPUtility::kRtcpNotValidCode)
    {
        const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet();
        if (pktType == RTCPUtility::kRtcpRtpfbTmmbnCode)
        {
            assert(0 == rtcpPacket.TMMBN.SenderSSRC);
            assert(0 == rtcpPacket.TMMBN.MediaSSRC);
            numItems = 0;
        }
        if (pktType == RTCPUtility::kRtcpRtpfbTmmbnItemCode)
        {
            boundingSet->ptrTmmbrSet[numItems]    = rtcpPacket.TMMBNItem.MaxTotalMediaBitRate;
            boundingSet->ptrPacketOHSet[numItems] = rtcpPacket.TMMBNItem.MeasuredOverhead;
            boundingSet->ptrSsrcSet[numItems]     = rtcpPacket.TMMBNItem.SSRC;
            ++numItems;
        }
        pktType = rtcpParser.Iterate();
    }
    return numItems;
};

WebRtc_Word32 GetFile(char* fileName)
{
    if (!fileName[0])
    {
        return 0;
    }

    FILE* openFile = fopen(fileName, "rb");
    assert(openFile != NULL);
    fseek(openFile, 0, SEEK_END);
    int len = (WebRtc_Word16)(ftell(openFile));
    rewind(openFile);
    assert(len > 0 && len < maxFileLen);
    fread(dataFile, 1, len, openFile);
    fclose(openFile);
    return len;
};


class LoopBackTransport2 : public webrtc::Transport, private TMMBRHelp
{
public:
    LoopBackTransport2(RtpRtcp* rtpRtcpModule)  :
      TMMBRHelp(false),
      _rtpRtcpModule(rtpRtcpModule),
      _cnt(0)
    {
    }
    virtual int SendPacket(int channel, const void *data, int len)
    {
        if( 0  == _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len))
        {
            return len;
        }
        return -1;
    }
    virtual int SendRTCPPacket(int channel, const void *data, int len)
    {
        char fileName[256] = {0};
        TMMBRSet* boundingSet = BoundingSet();
        boundingSet->VerifyAndAllocateSet(3);

        if (_cnt == 0)
        {
            // TMMBN {}
            // TMMBN {}
            // TMMBN {}
            // TMMBN {2,4,0} -> {4,2}
            assert(2 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set4);
            Verify(boundingSet, 1, set2);

            strcpy(fileName, "RTCPPacketTMMBR3.bin");
        }

        ++_cnt;

        // Get stored rtcp packet w/ TMMBR
        len = GetFile(fileName);
        if (len == 0)
        {
            return 1;
        }

        // Send in bitrate request
        if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len) == 0)
        {
            return len;
        }
        return -1;
    }
    RtpRtcp* _rtpRtcpModule;
    WebRtc_UWord32       _cnt;
};


class LoopBackTransportVideo : public webrtc::Transport, private TMMBRHelp
{
public:
    LoopBackTransportVideo(RtpRtcp* rtpRtcpModule)  :
      TMMBRHelp(false),
      _rtpRtcpModule(rtpRtcpModule),
      _cnt(0)
    {
    }
    virtual int SendPacket(int channel, const void *data, int len)
    {
        if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len)== 0)
        {
            return len;
        }
        return -1;
    }
    virtual int SendRTCPPacket(int channel, const void *data, int len)
    {
        char fileName[256] = {0};
        TMMBRSet* boundingSet = BoundingSet();
        boundingSet->VerifyAndAllocateSet(3);

        if (_cnt == 0)
        {
            strcpy(fileName, "RTCPPacketTMMBR0.bin");
        }
        else if (_cnt == 1)
        {
            // TMMBN {0} -> {0}
            assert(1 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set0);

            strcpy(fileName, "RTCPPacketTMMBR1.bin");
        }
        else if (_cnt == 2)
        {
            // TMMBN {0,1} -> {1}
            assert(1 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set1);

            strcpy(fileName, "RTCPPacketTMMBR2.bin");
        }
        else if (_cnt == 3)
        {
            // TMMBN {0,1,2} -> {2}
            assert(1 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set2);

            strcpy(fileName, "RTCPPacketTMMBR3.bin");
        }
        else if (_cnt == 4)
        {
            // TMMBN {0,1,2,3} -> {3,2}
            assert(2 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set3);
            Verify(boundingSet, 1, set2);

            strcpy(fileName, "RTCPPacketTMMBR4.bin");
        }
        else if (_cnt == 5)
        {
            // TMMBN {0,1,2,3,4} -> {3,4,2}
            assert(3 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set3);
            Verify(boundingSet, 1, set4);
            Verify(boundingSet, 2, set2);

            strcpy(fileName, "RTCPPacketTMMBR5.bin");
        }
        else if (_cnt == 6)
        {
            // TMMBN {0,1,2,3,4,5} -> {3,4,2}
            assert(3 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set3);
            Verify(boundingSet, 1, set4);
            Verify(boundingSet, 2, set2);

            strcpy(fileName, "RTCPPacketTMMBR4_2.bin");
        }
        else if (_cnt == 7)
        {
            // TMMBN {0,1,2,3,4_2,5} -> {4_2}
            assert(1 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set4_2);

            ++_cnt;
            ::Sleep(5*RTCP_INTERVAL_AUDIO_MS + 1000); // time out receiver
            _rtpRtcpModule->Process();             // SendRTCP() (_cnt == 8)
                                                   // a receiver has timed out -> UpdateTMMBR()
        }
        else if (_cnt == 8)
        {
            // No TMMBN in this packet
            assert(-1 == ParseRTCPPacket(data, len, boundingSet));
        }
        else if (_cnt == 10)
        {
            // TMMBN {} -> {}, empty set
            assert(0 == ParseRTCPPacket(data, len, boundingSet));

            strcpy(fileName, "RTCPPacketTMMBR2.bin");
        }
        else if (_cnt == 11)
        {
            // TMMBN {2} -> {2}
            assert(1 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set2);
        }
        else if (_cnt == 12) // ----- multi module -------------
        {
            // No TMMBN in this packet
            assert(-1 == ParseRTCPPacket(data, len, boundingSet));

            strcpy(fileName, "RTCPPacketTMMBR4.bin");
        }
        else if (_cnt == 13)
        {
            // TMMBN {}
            // TMMBN {}
            // TMMBN {}
            // TMMBN {2,4} -> {4,2}
            assert(2 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set4);
            Verify(boundingSet, 1, set2);

            strcpy(fileName, "RTCPPacketTMMBR0.bin");
        }
        else if (_cnt == 14)
        {
            // TMMBN {}
            // TMMBN {3}
            // TMMBN {}
            // TMMBN {2,4,0} -> {3,4,2}
            assert(3 == ParseRTCPPacket(data, len, boundingSet));
            Verify(boundingSet, 0, set3);
            Verify(boundingSet, 1, set4);
            Verify(boundingSet, 2, set2);

            strcpy(fileName, "RTCPPacketTMMBR1.bin");
        }
        //else if (_cnt == 15)
        //{
        //    // TMMBN {}
        //    // TMMBN {}
        //    // TMMBN {}
        //    // TMMBN {2,4,0,1} -> {4,2}
        //    //assert(2 == ParseRTCPPacket(data, len, boundingSet));
        //    //Verify(boundingSet, 0, set4);
        //    //Verify(boundingSet, 1, set2);
        //}
        //else if (_cnt == 15)
        //{
        //    // No TMMBN in this packet
        //    assert(-1 == ParseRTCPPacket(data, len, boundingSet));
        //}
        else if (_cnt == 15)
        {
            // TMMBN {}
            // TMMBN {}
            // TMMBN {}
            // TMMBN {} -> {}, empty set
            assert(0 == ParseRTCPPacket(data, len, boundingSet));
        }

        ++_cnt;

        // Get stored rtcp packet w/ TMMBR
        len = GetFile(fileName);
        if (len == 0)
        {
            return 1;
        }

        // Send in bitrate request
        if( 0 == _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len))
        {
            return len;
        }
        return -1;
    }

    RtpRtcp* _rtpRtcpModule;
    WebRtc_UWord32       _cnt;
};

class TestTMMBR : private TMMBRHelp
{
public:
    TestTMMBR() : TMMBRHelp(false) {};

    void Add(TMMBRSet* candidateSet, int index, InputSet set)
    {
        candidateSet->ptrTmmbrSet[index]    = set.TMMBR;
        candidateSet->ptrPacketOHSet[index] = set.packetOH;
        candidateSet->ptrSsrcSet[index]     = set.SSRC;
    };

    void Start()
    {
        // Get sets
        TMMBRSet* candidateSet = CandidateSet();
        assert(0 == candidateSet->sizeOfSet);
        TMMBRSet* boundingSet = BoundingSet();
        assert(0 == boundingSet->sizeOfSet);
        TMMBRSet* boundingSetToSend = BoundingSetToSend();
        assert(0 == boundingSetToSend->sizeOfSet);

        WebRtc_Word32 numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(0 == numBoundingSet); // should be empty

        assert( 0 == SetTMMBRBoundingSetToSend(NULL,0));        // ok to send empty set
        assert( 0 == SetTMMBRBoundingSetToSend(boundingSet,0)); // ok to send empty set

        WebRtc_UWord32 minBitrateKbit = 0;
        WebRtc_UWord32 maxBitrateKbit = 0;
        assert(-1 == CalcMinMaxBitRate(0, 0, 1, false, minBitrateKbit, maxBitrateKbit)); // no bounding set

        // ---------------------------------
        // Test candidate set {0} -> {0}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(1);
        assert(1 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(1 == numBoundingSet);
        Verify(boundingSet, 0, set0);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, 0));   // incorrect length
        assert(!IsOwner(set1.SSRC, 100)); // incorrect length

        assert( IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set0);

        // Get net bitrate depending on packet rate
        assert( 0 == CalcMinMaxBitRate(0, numBoundingSet, false,0, minBitrateKbit, maxBitrateKbit));
        assert(set0.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);
        assert(0 == CalcMinMaxBitRate(0, 100, false,0, minBitrateKbit, maxBitrateKbit));  // incorrect length
        assert(set0.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {0,1} -> {1}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(2);
        assert(2 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);
        Add(candidateSet, 1, set1);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(1 == numBoundingSet);
        Verify(boundingSet, 0, set1);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert( IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set1);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set1.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {0,1,2} -> {2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(3);
        assert(3 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);
        Add(candidateSet, 1, set1);
        Add(candidateSet, 2, set2);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(1 == numBoundingSet);
        Verify(boundingSet, 0, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set2.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {0,1,2,3} -> {3,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(4);
        assert(4 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);
        Add(candidateSet, 1, set1);
        Add(candidateSet, 2, set2);
        Add(candidateSet, 3, set3);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {0,1,2,3,4} -> {3,4,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(5);
        assert(5 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);
        Add(candidateSet, 1, set1);
        Add(candidateSet, 2, set2);
        Add(candidateSet, 3, set3);
        Add(candidateSet, 4, set4);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(3 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set4);
        Verify(boundingSet, 2, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set4);
        Verify(boundingSetToSend, 2, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {0,1,2,3,4,5} -> {3,4,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(6);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set0);
        Add(candidateSet, 1, set1);
        Add(candidateSet, 2, set2);
        Add(candidateSet, 3, set3);
        Add(candidateSet, 4, set4);
        Add(candidateSet, 5, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(3 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set4);
        Verify(boundingSet, 2, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set4);
        Verify(boundingSetToSend, 2, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set0.TMMBR == maxBitrateKbit);


        // ---------------------------------
        // Test candidate set {1,2,3,4,5} -> {3,4,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(5);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set2);
        Add(candidateSet, 2, set3);
        Add(candidateSet, 3, set4);
        Add(candidateSet, 4, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(3 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set4);
        Verify(boundingSet, 2, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        assert(boundingSetToSend->sizeOfSet == numBoundingSet);
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set4);
        Verify(boundingSetToSend, 2, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);


        // ---------------------------------
        // Test candidate set {1,3,4,5} -> {3,4}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(4);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set3);
        Add(candidateSet, 2, set4);
        Add(candidateSet, 3, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set4);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set4);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet,true,0,  minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {1,2,4,5} -> {4,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(4);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set2);
        Add(candidateSet, 2, set4);
        Add(candidateSet, 3, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set4);
        Verify(boundingSet, 1, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert(!IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set4);
        Verify(boundingSetToSend, 1, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set4.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {1,2,3,5} -> {3,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(4);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set2);
        Add(candidateSet, 2, set3);
        Add(candidateSet, 3, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert(!IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {1,2,3,4_1,5} -> {3,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(5);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set2);
        Add(candidateSet, 2, set3);
        Add(candidateSet, 3, set4_1);
        Add(candidateSet, 4, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set3);
        Verify(boundingSet, 1, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert( IsOwner(set3.SSRC, numBoundingSet));
        assert(!IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set3);
        Verify(boundingSetToSend, 1, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set3.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {1,2,3,4_2,5} -> {4_2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(5);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set1);
        Add(candidateSet, 1, set2);
        Add(candidateSet, 2, set3);
        Add(candidateSet, 3, set4_2);
        Add(candidateSet, 4, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(1 == numBoundingSet);
        Verify(boundingSet, 0, set4_2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));
        assert(!IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set4_2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(MIN_VIDEO_BW_MANAGEMENT_BITRATE == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {} -> {}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(0);
        assert(6 == candidateSet->sizeOfSet);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(0 == numBoundingSet);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));
        assert(!IsOwner(set3.SSRC, numBoundingSet));
        assert(!IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();

        // Get net bitrate depending on packet rate
        assert(-1 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));

        // ---------------------------------
        // Test candidate set {x0,5} -> {5}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(2);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set00);
        Add(candidateSet, 1, set5);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(1 == numBoundingSet);
        Verify(boundingSet, 0, set5);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert(!IsOwner(set2.SSRC, numBoundingSet));
        assert(!IsOwner(set3.SSRC, numBoundingSet));
        assert(!IsOwner(set4.SSRC, numBoundingSet));
        assert( IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set5);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set5.TMMBR == minBitrateKbit);
        assert(set5.TMMBR == maxBitrateKbit);

        // ---------------------------------
        // Test candidate set {x0,4,2} -> {4,2}
        // ---------------------------------
        candidateSet = VerifyAndAllocateCandidateSet(3);
        assert(6 == candidateSet->sizeOfSet);
        Add(candidateSet, 0, set00);
        Add(candidateSet, 1, set4);
        Add(candidateSet, 2, set2);

        // Find bounding set
        numBoundingSet = FindTMMBRBoundingSet(boundingSet);
        assert(2 == numBoundingSet);
        Verify(boundingSet, 0, set4);
        Verify(boundingSet, 1, set2);

        // Is owner of set
        assert(!IsOwner(set0.SSRC, numBoundingSet));
        assert(!IsOwner(set1.SSRC, numBoundingSet));
        assert( IsOwner(set2.SSRC, numBoundingSet));
        assert(!IsOwner(set3.SSRC, numBoundingSet));
        assert( IsOwner(set4.SSRC, numBoundingSet));
        assert(!IsOwner(set5.SSRC, numBoundingSet));

        // Set boundingSet to send
        assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate));

        // Get boundingSet to send
        boundingSetToSend = BoundingSetToSend();
        Verify(boundingSetToSend, 0, set4);
        Verify(boundingSetToSend, 1, set2);

        // Get net bitrate depending on packet rate
        assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit));
        assert(set4.TMMBR == minBitrateKbit);
        assert(set2.TMMBR == maxBitrateKbit);
    };
};

class NULLDataZink: public RtpData
{
    virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
                                              const WebRtc_UWord16 payloadSize,
                                              const webrtc::WebRtcRTPHeader* rtpHeader,
                                              const WebRtc_UWord8* incomingRtpPacket,
                                              const WebRtc_UWord16 incomingRtpPacketLengt)
    {
        return 0;
    };
};


int _tmain(int argc, _TCHAR* argv[])
{

    std::string str;
    std::cout << "------------------------" << std::endl;
    std::cout << "------ Test TMMBR ------" << std::endl;
    std::cout << "------------------------" << std::endl;
    std::cout << "  "  << std::endl;

    // --------------------
    // Test TMMBRHelp class
    // --------------------
    TestTMMBR test;
    test.Start();

    printf("TMMBRHelp-class test done.\n");

    // ------------------------
    // Test TMMBR single module
    // ------------------------
    RtpRtcp* rtpRtcpModuleVideo = RtpRtcp::CreateRtpRtcp(0, false);

    LoopBackTransportVideo* myLoopBackTransportVideo = new LoopBackTransportVideo(rtpRtcpModuleVideo);
    assert(0 == rtpRtcpModuleVideo->RegisterSendTransport(myLoopBackTransportVideo));

    assert(false == rtpRtcpModuleVideo->TMMBR());
    rtpRtcpModuleVideo->SetTMMBRStatus(true);
    assert(true == rtpRtcpModuleVideo->TMMBR());

    assert(0 == rtpRtcpModuleVideo->RegisterSendPayload( "I420", 96));
    assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload( "I420", 96));

    // send a RTP packet with SSRC 11111 to get 11111 as the received SSRC
    assert(0 == rtpRtcpModuleVideo->SetSSRC(11111));
    const WebRtc_UWord8 testStream[9] = "testtest";
    assert(0 == rtpRtcpModuleVideo->RegisterIncomingDataCallback(new NULLDataZink())); // needed to avoid error from parsing the incoming stream
    assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameKey,96, 0, testStream, 8));

    // set the SSRC to 0
    assert(0 == rtpRtcpModuleVideo->SetSSRC(0));

    //
    assert(0 == rtpRtcpModuleVideo->SetRTCPStatus(kRtcpCompound));

    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {0}                     // should this make us remember a TMMBR?
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {1},   verify TMMBN {0}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {2},   verify TMMBN {1}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {3},   verify TMMBN {2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {4},   verify TMMBN {3,2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {5},   verify TMMBN {3,4,2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {4_2}, verify TMMBN {3,4,2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> time out receivers,   verify TMMBN {4_2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // -> incoming TMMBR {2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());  // ->                       verify TMMBN {2}

    printf("Single module test done.\n");

    // ------------------------
    // Test TMMBR multi module
    // ------------------------
    RtpRtcp* rtpRtcpModuleVideoDef = RtpRtcp::CreateRtpRtcp(10, false);
    assert(0 == rtpRtcpModuleVideo->RegisterDefaultModule(rtpRtcpModuleVideoDef));

    RtpRtcp* rtpRtcpModuleVideo1 = RtpRtcp::CreateRtpRtcp(1, false);
    assert(0 == rtpRtcpModuleVideo1->RegisterDefaultModule(rtpRtcpModuleVideoDef));

    RtpRtcp* rtpRtcpModuleVideo2 = RtpRtcp::CreateRtpRtcp(2, false);
    assert(0 == rtpRtcpModuleVideo2->RegisterDefaultModule(rtpRtcpModuleVideoDef));

    RtpRtcp* rtpRtcpModuleVideo3 = RtpRtcp::CreateRtpRtcp(3, false);
    assert(0 == rtpRtcpModuleVideo3->RegisterDefaultModule(rtpRtcpModuleVideoDef));

    LoopBackTransport2* myLoopBackTransport2 = new LoopBackTransport2(rtpRtcpModuleVideo2);
    assert(0 == rtpRtcpModuleVideo2->RegisterSendTransport(myLoopBackTransport2));

    assert(0 == rtpRtcpModuleVideo2->SetRTCPStatus(kRtcpCompound));

    // set the SSRC to 0
    assert(0 == rtpRtcpModuleVideo2->SetSSRC(0));

    assert(0 == rtpRtcpModuleVideo->SendRTCP());   // -> incoming TMMBR {4}, verify no TMMBN in this packet
    assert(0 == rtpRtcpModuleVideo->SendRTCP());   // -> incoming TMMBR {0}, verify TMMBN {4,2}
    assert(0 == rtpRtcpModuleVideo2->SendRTCP());  // -> incoming TMMBR {3}, verify TMMBN {4,2}
    assert(0 == rtpRtcpModuleVideo->SendRTCP());   // -> incoming TMMBR {1}, verify TMMBN {3,4,2}
    ::Sleep(5*RTCP_INTERVAL_AUDIO_MS + 1000);
    rtpRtcpModuleVideo2->Process();                // time out receiver2 -> UpdateTMMBR()
    assert(0 == rtpRtcpModuleVideo->SendRTCP());   // verify TMMBN {}

    printf("Multi module test done.\n");


    RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo);
    RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo1);
    RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo2);
    RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo3);
    RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideoDef);

    TEST_PASSED();
    ::Sleep(5000);

    return 0;
}

