// Copyright (C) 2001,2002 Federico Montesino <p5087@quintero.fie.us.es>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however    
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.    
//
// This exception applies only to the code released under the name GNU
// ccRTP.  If you copy code from other releases into a copy of GNU
// ccRTP, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccRTP, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

/** 
 * @file members.cpp
 * @shot MembershipBookkeeping class implementation
 *
 * @todo implement the reallocation mechanism (e.g., when the number
 * of ssrcs per collision list goes up to 2, make the size
 * approx. four times bigger (0.5 ssrcs per list now. when the number
 * of ssrcs per list goes down to 0.5, decrease four times. Do not use
 * 2 or 0.5, but `2 + something' and `0.5 - somehting'). Always
 * jumping between prime numbers -> provide a table from 7 to many.
 **/

#include "private.h"
#include <ccrtp/cqueue.h>

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

const uint32 MembershipBookkeeping::SyncSourceLink::SEQNUMMOD = (1<<16);

MembershipBookkeeping::SyncSourceLink::~SyncSourceLink()
{ 
#ifdef	CCXX_EXCEPTIONS
	try {
#endif
		delete source;
		delete prevConflict;
		delete receiverInfo;
		delete senderInfo;
#ifdef	CCXX_EXCEPTIONS
	} catch (...) { }
#endif
}
	
void
MembershipBookkeeping::SyncSourceLink::initStats()
{
	lastPacketTime.tv_sec = lastPacketTime.tv_usec = 0;
	lastRTCPPacketTime.tv_sec = lastRTCPPacketTime.tv_usec = 0;
	lastRTCPSRTime.tv_sec = lastRTCPSRTime.tv_usec = 0;

	senderInfo = NULL;
	receiverInfo = NULL;

	obsPacketCount = obsOctetCount = 0;
	maxSeqNum = extendedMaxSeqNum = 0;
	cumulativePacketLost = 0;
	fractionLost = 0;
	jitter = 0;
	initialDataTimestamp = 0;
	initialDataTime.tv_sec = initialDataTime.tv_usec = 0;
	flag = false;

	badSeqNum = SEQNUMMOD + 1;
	probation = 0;
	baseSeqNum = 0;
	expectedPrior = 0;
	receivedPrior = 0;
	seqNumAccum = 0;
}

void
MembershipBookkeeping::SyncSourceLink::computeStats()
{
	// See Appendix A.3
	
	// compute cumulative packet lost.
	setExtendedMaxSeqNum(getMaxSeqNum() + getSeqNumAccum());
	uint32 expected = 
		(getExtendedMaxSeqNum() - getBaseSeqNum() + 1);
	uint32 pc = getObservedPacketCount();
	uint32 lost;
	if ( 0 == pc )
		lost = 0;
	else
		lost = expected - pc;
	setCumulativePacketLost(lost);
	
	// compute the fraction of packets lost during the last
	// reporting interval.
	uint32 expectedDelta = expected - expectedPrior;
	expectedPrior = expected;
	uint32 receivedDelta = getObservedPacketCount() -
		receivedPrior;
	receivedPrior = getObservedPacketCount();
	uint32 lostDelta = expectedDelta - receivedDelta;
	if ( expectedDelta == 0 || lostDelta <= 0 ) 
		setFractionLost(0);
	else 
		setFractionLost((lostDelta<<8) / expectedDelta );
}

void
MembershipBookkeeping::SyncSourceLink::setPrevConflict(InetAddress& addr, 
						       tpport_t dataPort,
						       tpport_t controlPort)
{
	delete prevConflict;
	prevConflict = 
		new ConflictingTransportAddress(addr,dataPort,controlPort);
}

void
MembershipBookkeeping::SyncSourceLink::
recordInsertion(const IncomingRTPPktLink&)
{
}

void
MembershipBookkeeping::SyncSourceLink::
setSenderInfo(unsigned char* si)
{
	if ( NULL == senderInfo )
		senderInfo = reinterpret_cast<unsigned char*>
			(new RTCPCompoundHandler::SenderInfo);
	memcpy(senderInfo,si,sizeof(RTCPCompoundHandler::SenderInfo));
}

void
MembershipBookkeeping::SyncSourceLink::
setReceiverInfo(unsigned char* ri)
{
	if ( NULL == receiverInfo )
		receiverInfo = reinterpret_cast<unsigned char*>
			(new RTCPCompoundHandler::ReceiverInfo);
	memcpy(receiverInfo,ri,sizeof(RTCPCompoundHandler::ReceiverInfo));
}

const size_t MembershipBookkeeping::defaultMembersHashSize = 11;
const uint32 MembershipBookkeeping::SEQNUMMOD = (1<<16);

#define HASH(a) ((a + (a >> 8)) % MembershipBookkeeping::sourceBucketsNum)

// Initializes the array (hash table) and the global list of
// SyncSourceLink objects
MembershipBookkeeping::MembershipBookkeeping(uint32 initialSize):
	SyncSourceHandler(), ParticipantHandler(), 
	ConflictHandler(), Members(),
	sourceBucketsNum(initialSize),
	sourceLinks( new SyncSourceLink* [sourceBucketsNum] ),
	first(NULL), last(NULL)
{
	for ( uint32 i = 0; i < sourceBucketsNum; i++ )
		sourceLinks[i] = NULL;
}

void 
MembershipBookkeeping::endMembers()
{
	SyncSourceLink* s;
	while( first ) {
		s = first;
		first = first->next;
#ifdef	CCXX_EXCEPTIONS
		try {
#endif
			delete s;
#ifdef	CCXX_EXCEPTIONS
		} catch (...) {}
#endif
	}
	last = NULL;
#ifdef	CCXX_EXCEPTIONS
	try {
#endif
		delete [] sourceLinks;
#ifdef	CCXX_EXCEPTIONS
	} catch (...) {}
#endif
}

bool
MembershipBookkeeping::isRegistered(uint32 ssrc)
{
	bool result = false;
	SyncSourceLink* sl = sourceLinks[ HASH(ssrc) ];

	while ( sl != NULL ) {
		if ( ssrc == sl->getSource()->getID() ) {
			result = true;
			break;
		} else if ( ssrc < sl->getSource()->getID() ) {
			break;
		} else {
			// keep on searching
			sl = sl->getNextCollis();
		}
	}
	return result;
}

// Gets or creates the source and its link structure.
MembershipBookkeeping::SyncSourceLink*
MembershipBookkeeping::getSourceBySSRC(uint32 ssrc, bool& created)
{
	uint32 hashing = HASH(ssrc);
	SyncSourceLink* result = sourceLinks[hashing];
	SyncSourceLink* prev = NULL;
	created = false;

	if ( NULL == result ) {
		result = sourceLinks[hashing] = 
			new SyncSourceLink(this,new SyncSource(ssrc));
		created = true;
	} else {
		while ( NULL != result ) {
			if ( ssrc == result->getSource()->getID() ) {
				// we found it!
				break;
			} else if ( ssrc > result->getSource()->getID() ) {
				// keep on searching
				prev = result;
				result = result->getNextCollis();
			} else { 
				// ( ssrc < result->getSource()->getID() )
				// it isn't recorded here -> create it.
				SyncSourceLink* newlink = 
					new SyncSourceLink(this,new SyncSource(ssrc));
				if ( NULL != prev )
					prev->setNextCollis(newlink);
				else
					sourceLinks[hashing] = newlink;
				newlink->setNextCollis(result);
				result = newlink;
				created = true;
				break;
			}
		}
		if ( NULL == result ) {
			// insert at the end of the collision list
			result = 
				new SyncSourceLink(this,new SyncSource(ssrc));
			created = true;
			prev->setNextCollis(result);
		}
	}
	if ( created ) {
		if ( first )
			last->setNext(result);			
		else
			first =  result;
		last = result;
		increaseMembersCount();
	}

	return result;
}

bool
MembershipBookkeeping::BYESource(uint32 ssrc) 
{  
	bool found = false;
	// If the source identified by ssrc is in the table, mark it
	// as leaving the session. If it was not, do nothing.
	if ( isRegistered(ssrc) ) {
		found = true;
		decreaseMembersCount(); // TODO really decrease right now?
	}
	return found;
}

bool
MembershipBookkeeping::removeSource(uint32 ssrc) 
{  
	bool found = false;
	SyncSourceLink* old = NULL, 
		* s = sourceLinks[ HASH(ssrc) ]; 
	while ( s != NULL ){
		if ( s->getSource()->getID() == ssrc ) {
			// we found it
			if ( old )
				old->setNextCollis(s->getNextCollis());
			if ( s->getPrev() )
				s->getPrev()->setNext(s->getNext());
			if ( s->getNext() )
				s->getNext()->setPrev(s->getPrev());
			decreaseMembersCount();
			if ( s->getSource()->isSender() )
				decreaseSendersCount();
			delete s;
			found = true;
			break;              
		} else if ( s->getSource()->getID() > ssrc ) {
			// it wasn't here
			break;
		} else {
			// keep on searching
			old = s;
			s = s->getNextCollis();
		}
	}
	return found;
}

#ifdef	CCXX_NAMESPACES
}
#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1