/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */

static const char* const RtcpReceiver_cxx_Version =
    "$Id: RtcpReceiver.cxx,v 1.11 2001/08/10 04:02:15 icahoon Exp $";


#include "global.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <sys/types.h>
#include "vtypes.h"
#include <unistd.h>
#include <string.h>
#include <map>

// networking
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "cpLog.h"
#include "vsock.hxx"

#include "rtpTypes.h"
#include "rtpTools.hxx"
#include "NtpTime.hxx"
#include "Rtp.hxx"
#include "Rtcp.hxx"




/* ----------------------------------------------------------------- */
/* --- rtpcReceiver Constructor ------------------------------------ */
/* ----------------------------------------------------------------- */

RtcpReceiver::RtcpReceiver (int localMinPort, int localMaxPort)
{
    myStack = new UdpStack(NULL, localMinPort, localMaxPort);
    freeStack = true;

    constructRtcpReceiver();
}

RtcpReceiver::RtcpReceiver (int localPort)
{
    myStack = new UdpStack(NULL, localPort) ;
    freeStack = true;

    constructRtcpReceiver();
}

RtcpReceiver::RtcpReceiver (UdpStack* udp)
{
    myStack = udp;
    freeStack = false;

    constructRtcpReceiver();
}


void RtcpReceiver::constructRtcpReceiver ()
{

    packetReceived = 0;

    accumOneWayDelay = 0;
    avgOneWayDelay = 0;
    accumRoundTripDelay = 0;
    avgRoundTripDelay = 0;
}


RtcpReceiver::~RtcpReceiver ()
{
    if (freeStack)
    {
        delete myStack;
        myStack = 0;
    }

    // must remove each transmitter block and each SDES info
    map < RtpSrc, RtpTranInfo* > ::iterator s = tranInfoList.begin();
    while (s != tranInfoList.end())
    {
        removeTranInfo((s->second)->ssrc);
        s = tranInfoList.begin();
    }
    //cpLog(LOG_DEBUG_STACK, "RTCP: Receiver removed");
}



/* --- receive packet functions ------------------------------------ */


RtcpPacket* RtcpReceiver::getPacket ()
{
    // check for network activity
    fd_set netFD;
    FD_ZERO (&netFD);
    FD_SET (myStack->getSocketFD(), &netFD);
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    int selret = select (myStack->getSocketFD() + 1,
                         &netFD, NULL, NULL, &timeout);
    if (selret <= 0)
    {
        // select error
        if (selret < 0) perror ("rtcpReceiver select");
        else
            // no network activity
            return NULL;
    }
    else
    {
        //cpLog(LOG_DEBUG_STACK, "Get RTCP packet");
    }

    // create packet

    RtcpPacket* p = new RtcpPacket ();

    // receive packet
    //unsigned int fromLen = sizeof (txAddress);
    //int len = recvfrom (socketFD, p->getPacketData(), p->getPacketAlloc(),
    //                    0, (struct sockaddr*) &txAddress, &fromLen);
    //    int len = myStack->receive  (p->getPacketData(), p->getPacketAlloc());
    NetworkAddress sender;
    int len = myStack->receiveFrom (p->getPacketData(),
                                    p->getPacketAlloc(),
                                    &sender);
    //cpLog(LOG_DEBUG_STACK, "RTCP receiveFrom: %s : %d", sender.getIpName().c_str(),
    //          sender.getPort());

    // ? RtpTime arrival = generateTime ();
    if (len <= 0)
    {
        delete p;
        p = NULL;
        return NULL;
    }
    p->setTotalUsage (len);

    // kudo
    //    p->printPacket();

    // check packet
    if (!isValid(p))
    {
        delete p;
        p = NULL;
        return NULL;
    }
    return p;
}



int RtcpReceiver::isValid (RtcpPacket* p)
{
    char* begin = reinterpret_cast < char* > (p->getPacketData());
    char* end = reinterpret_cast < char* > (begin + p->getTotalUsage());
    RtcpHeader* middle = reinterpret_cast < RtcpHeader* > (begin);

    // check if known payload type for first packet
    if (middle->type != rtcpTypeSR && middle->type != rtcpTypeRR)
        return 0;

    // check padbyte
    if (middle->padding)
        return 0;

    // check header lengths
    while (begin < end && (int)middle->version == RTP_VERSION)
    {
        begin += (ntohs(middle->length) + 1) * sizeof(RtpSrc);
        middle = reinterpret_cast < RtcpHeader* > (begin);
    }

    if (begin != end)
        return 0;

    // exit with success
    cpLog(LOG_DEBUG_STACK, "RTCP packet is valid");
    //    cout << "RTCP packet is valid" << endl;
    return 1;
}



int RtcpReceiver::readRTCP (RtcpPacket* p)
{

    char* begin = reinterpret_cast < char* > (p->getPacketData());
    char* end = reinterpret_cast < char* > (begin + p->getTotalUsage());
    RtcpHeader* middle = NULL;
    int ret = 0;

    while (begin < end)
    {
        middle = reinterpret_cast < RtcpHeader* > (begin);
        switch (middle->type)
        {
            case (rtcpTypeSR):
                        case (rtcpTypeRR):
                            readSR (middle);
            break;
            case (rtcpTypeSDES):
                        readSDES (middle);
            break;
            case (rtcpTypeBYE):
                       if ( readBYE (middle) == 0)
                       {
                           ret = 1;
                       }
            break;
            case (rtcpTypeAPP):
                        readAPP (middle);
            break;
            default:
            cpLog (LOG_ERR, "RTCP: Unknown RTCP type");
            break;
        }
        begin += (ntohs(middle->length) + 1) * sizeof(u_int32_t);
    }
    return ret;
}



RtcpHeader* RtcpReceiver::findRTCP (RtcpPacket* p, RtcpType type)
{
    char* begin = reinterpret_cast < char* > (p->getPacketData());
    char* end = reinterpret_cast < char* > (begin + p->getTotalUsage());
    RtcpHeader* middle = NULL;

    while (begin < end)
    {
        middle = reinterpret_cast < RtcpHeader* > (begin);
        if (type == static_cast < RtcpType > (middle->type))
            return middle;
        begin += (ntohs(middle->length) + 1) * sizeof(u_int32_t);
    }

    // packet type not found
    cpLog (LOG_ERR, "RTCP: Type found here: %d", (int)type);
    return NULL;
}



/* --- Read SR RTCP packet ----------------------------------------- */

int RtcpReceiver::readSR (RtcpPacket* p)
{
    RtcpHeader* head = findRTCP (p, rtcpTypeSR);
    if (head == NULL) head = findRTCP (p, rtcpTypeRR);
    if (head == NULL) return -1;

    readSR (head);

    // read next RR packet if found
    // future: - ?

    return 0;
}


void RtcpReceiver::readSR (RtcpHeader* head)
{
    char* middle = NULL;

    NtpTime nowNtp = getNtpTime();

    // read SR block
    if (head->type == rtcpTypeSR)
    {
        RtcpSender* senderBlock = reinterpret_cast < RtcpSender* >
                                  ((char*)head + sizeof(RtcpHeader));
        RtpTranInfo* s = findTranInfo(ntohl(senderBlock->ssrc));
        s->lastSRTimestamp = (ntohl(senderBlock->ntpTimeSec) << 16 |
                              ntohl(senderBlock->ntpTimeFrac) >> 16);
        s->recvLastSRTimestamp = nowNtp;

        //printSR (senderBlock);  // - debug

        packetReceived++;

        NtpTime thenNtp ( ntohl(senderBlock->ntpTimeSec),
                          ntohl(senderBlock->ntpTimeFrac) );

        accumOneWayDelay += (nowNtp - thenNtp);

        avgOneWayDelay = accumOneWayDelay / packetReceived;

        middle = (char*)head + sizeof(RtcpHeader) + sizeof(RtcpSender);
    }
    else
    {
        // move over blank SR header
        middle = (char*)head + sizeof(RtcpHeader);

        // move over the ssrc of packet sender
        RtpSrc* sender = reinterpret_cast < RtpSrc* > (middle);
        RtpSrc ssrc;

        ssrc = ntohl(*sender);
        middle += sizeof(RtpSrc);

        packetReceived++;
    }


    // read RR blocks
    RtcpReport* block = reinterpret_cast < RtcpReport* > (middle);
    for (int i = head->count; i > 0; i--)
    {
        //printRR (block);  // - debug

        // - ? what these are if the count is more than 1??
        NtpTime thenNtp (ntohl(block->lastSRTimeStamp) >> 16,
                         ntohl(block->lastSRTimeStamp) << 16 );

        NtpTime nowNtp1 (nowNtp.getSeconds() & 0x0000FFFF, 
                         nowNtp.getFractional() & 0xFFFF0000);
        accumRoundTripDelay += ((nowNtp1 - thenNtp)
                                - ntohl(block->lastSRDelay));

        avgRoundTripDelay = accumRoundTripDelay / packetReceived;

        ++block;
    }

    // handle profile specific extensions
    // - ?
}




void RtcpReceiver::printSR (RtcpSender* p)
{
    cerr << "Got SR from " << ntohl(p->ssrc) << endl;
    cerr << "  NTP time: " << ntohl(p->ntpTimeSec) << " ";
    cerr << ntohl(p->ntpTimeFrac) << endl;
    cerr << "  RTP time: " << ntohl(p->rtpTime) << endl;
    cerr << "  Packets sent: " << ntohl(p->packetCount);
    cerr << "    Payload sent: " << ntohl(p->octetCount) << endl;
}


void RtcpReceiver::printRR (RtcpReport* p)
{
    cerr << "Got RR for " << ntohl(p->ssrc) << endl;
    cerr << "  Lost Frac: " << p->fracLost;
    u_int32_t lost = (p->cumLost[0] << 16) | (p->cumLost[1] << 8) | p->cumLost[2];
    lost = lost & (0xffffff);
    cerr << "  Lost count: " << lost;
    cerr << "  Cycles: " << ntohs(p->recvCycles);
    cerr << "  Last seq: " << ntohs(p->lastSeqRecv) << endl;
    cerr << "  Jitter: " << ntohl(p->jitter) << "  ";
    cerr << "Last SR: " << ntohl(p->lastSRDelay) << endl;
}



/* --- Read SDES packet -------------------------------------------- */

int RtcpReceiver::readSDES (RtcpPacket* p)
{
    RtcpHeader* head = findRTCP (p, rtcpTypeSDES);
    if (head == NULL) return -1;

    readSDES (head);

    // read next SDES packet if found
    // future: - ?

    return 0;
}


void RtcpReceiver::readSDES (RtcpHeader* head)
{
    char* begin = reinterpret_cast < char* > ((char*)head + sizeof(RtcpHeader));
    RtcpChunk* middle = reinterpret_cast < RtcpChunk* > (begin);

    RtcpSDESItem* item = NULL;
    RtcpSDESItem* nextitem = NULL;
    RtpSrc ssrc;

    for (int i = head->count; i > 0; i--)
    {
        ssrc = ntohl(middle->ssrc);

        for (item = &(middle->startOfItem); item->type; item = nextitem)
        {
            addSDESItem(ssrc, item);
            nextitem = reinterpret_cast < RtcpSDESItem* >
                       ((char*)item + sizeof(RtcpSDESItem) - 1 + item->length);
        }

        middle = reinterpret_cast < RtcpChunk* > (item);
    }
}



void RtcpReceiver::addSDESItem (RtpSrc src, RtcpSDESItem* item)
{
    RtpTranInfo* s = findTranInfo(src);

    switch (item->type)
    {
        case rtcpSdesCname:
        strncpy ((s->SDESInfo).cname, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesName:
        strncpy ((s->SDESInfo).name, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesEmail:
        strncpy ((s->SDESInfo).email, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesPhone:
        strncpy ((s->SDESInfo).phone, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesLoc:
        strncpy ((s->SDESInfo).loc, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesTool:
        strncpy ((s->SDESInfo).tool, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesNote:
        strncpy ((s->SDESInfo).note, &(item->startOfText), item->length + 1);
        break;
        case rtcpSdesPriv:
        // future: not implemented
        default:
        cpLog (LOG_ERR, "RtcpReceiver: SDES type unknown");
        break;
    }

    /*
    // - debug
    cerr <<"Update "<<src<<" with "<< (int) item->type <<" "<< (int) item->length;
    char output [255];
    memset (output, 0, 255);
    strncpy (output, &(item->startOfText), item->length+1);
    cerr << endl <<output<<endl;
    cerr <<"_SDES_";
    */
}



/* --- Read BYE packet --------------------------------------------- */

int RtcpReceiver::readBYE (RtcpPacket* p)
{
    RtcpHeader* head = findRTCP (p, rtcpTypeBYE);
    if (head == NULL) return -1;

    readBYE (head);

    // read next BYE packet if found
    // future: - ?

    return 0;
}


int RtcpReceiver::readBYE (RtcpHeader* head)
{

    //    char* end = reinterpret_cast<char*>
    //                ((char*)head + sizeof(RtpSrc) * (ntohs(head->length) + 1));
    RtpSrc* src = reinterpret_cast < RtpSrc* >
                  ((char*)head + sizeof(RtcpHeader));


    for (int i = head->count; i > 0; i--)
    {
        cpLog( LOG_DEBUG_STACK, "readRtcpBye for %d", ntohl(*src) );
        //       cerr << "readRtcpBye for " << ntohl(*src) << endl;
        removeTranInfo (ntohl(*src++));
    }

    return 0;

    //TODO Convert this to cpLog if necessary
#if 0
    // print reason
    char* middle = reinterpret_cast < char* > (src);
    //    if (middle != end)
    {
        RtcpBye* reason = reinterpret_cast < RtcpBye* > (middle);
        cerr << "   Reason: ";    // - debug
        cerr.write(&(reason->startOfText), (int) reason->length);    // - debug
        cerr << endl;    // - debug
    }
    //cerr <<"_BYE_";  // - debug

#endif
}



/* --- Read APP packet --------------------------------------------- */

int RtcpReceiver::readAPP (RtcpPacket* p)
{
    RtcpHeader* head = findRTCP (p, rtcpTypeAPP);
    if (head == NULL) return -1;

    readAPP (head);

    // read next APP packet if found
    // future: - ?

    return 0;
}


void RtcpReceiver::readAPP (RtcpHeader* head)
{
    // future: not implemented
    assert (0);
}



/* --- known transmitter list functions ---------------------------- */

RtpTranInfo* RtcpReceiver::addTranInfo (RtpSrc src, RtpReceiver* recv)
{

    //    cout << "adding: " << src << endl;

    if (recv) assert (src == recv->ssrc);

    RtpTranInfo* s = new RtpTranInfo;
    s->recv = recv;
    s->ssrc = src;
    s->expectedPrior = 0;
    s->receivedPrior = 0;

    //    cout << "adding ptr: " << s << endl;

    if (addTranFinal (s))
    {
        delete s;  // - ?
        s = findTranInfo (src);
        assert (s->recv == NULL);  // - ?
        s->recv = recv;
    }
    return s;
}


int RtcpReceiver::addTranFinal (RtpTranInfo* s)
{
    // add transmitter to listing
    pair < map < RtpSrc, RtpTranInfo* > ::iterator, bool > result =
        tranInfoList.insert (pair < RtpSrc, RtpTranInfo* >
                             (s->ssrc, s));
    if (!result.second)
    {
        //transmitter already in listing
        return 1;
    }
    //cpLog (LOG_DEBUG_STACK, "RTCP: Transmitter add: %d", s->ssrc);
    return 0;
}



int RtcpReceiver::removeTranInfo (RtpSrc src, int flag)
{
    //    cout << "RTCP: removing: " << src << endl;

    map < RtpSrc, RtpTranInfo* > ::iterator p = tranInfoList.find(src);
    if (p == tranInfoList.end())
    {
        // src not found
        assert (0);
    }

    RtpTranInfo* info = p->second;

    //    cout << "RTCP: removing ptr: " << info << endl;

    // remove from RTP stack
    if (info->recv && !flag)
        (info->recv)->removeSource(info->ssrc, 1);
    info->recv = NULL;
    delete info; info = NULL;

    //    cout << "RTCP: done removing\n";

    // remove from receiver list
    tranInfoList.erase (p);

    //cpLog (LOG_DEBUG_STACK, "RTCP: Transmitter removed: %d", src);
    return 0;
}



RtpTranInfo* RtcpReceiver::findTranInfo (RtpSrc src)
{
    RtpTranInfo* info = NULL;

    map < RtpSrc, RtpTranInfo* > ::iterator p = tranInfoList.find(src);
    if (p == tranInfoList.end())
        // receiver not found, so add it
        info = addTranInfo(src);
    else
        info = p->second;

    return info;
}


RtpTranInfo* RtcpReceiver::getTranInfoList (int index)
{
    assert (index >= 0);
    assert (index < getTranInfoCount());

    map < RtpSrc, RtpTranInfo* > ::iterator p = tranInfoList.begin();
    for (int i = 0; i < index; i++)
        ++p;

    assert (p != tranInfoList.end());
    return p->second;
}


int RtcpReceiver::getTranInfoCount()
{
    return tranInfoList.size();
}



int RtcpReceiver::getPort ()
{
    return myStack->getRxPort();
};

int RtcpReceiver::getSocketFD ()
{
    return myStack->getSocketFD();
};


syntax highlighted by Code2HTML, v. 0.9.1