/*
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
    File:       RTPOverbufferWindow.cpp

    Contains:   Implementation of the class
    
    Written By: Denis Serenyi

*/

#include "RTPOverbufferWindow.h"
#include "OSMemory.h"
#include "MyAssert.h"

RTPOverbufferWindow::RTPOverbufferWindow(UInt32 inSendInterval, UInt32 inInitialWindowSize, UInt32 inMaxSendAheadTimeInSec,
									Float32 inOverbufferRate)
:   fWindowSize(inInitialWindowSize),
    fBytesSentSinceLastReport(0),
    fSendInterval(inSendInterval),
    fBytesDuringLastSecond(0),
    fLastSecondStart(-1),
    fBytesDuringPreviousSecond(0),
    fPreviousSecondStart(-1),
    fBytesDuringBucket(0),
    fBucketBegin(0),
    fBucketTimeAhead(0),
    fPreviousBucketTimeAhead(0),
    fMaxSendAheadTime(inMaxSendAheadTimeInSec * 1000),
    fWriteBurstBeginning(false),
	fOverbufferingEnabled(true),
	fOverbufferRate(inOverbufferRate),
	fSendAheadDurationInMsec(1000),
	fOverbufferWindowBegin(-1)
{
    if (fSendInterval == 0)
    {
        fOverbufferingEnabled = false;
        fSendInterval = 200;
    }
	
	if(fOverbufferRate < 1.0)
		fOverbufferRate = 1.0;

}

SInt64 RTPOverbufferWindow::CheckTransmitTime(const SInt64& inTransmitTime, const SInt64& inCurrentTime, SInt32 inPacketSize)
{
    // if this is the beginning of a bucket interval, roll over figures from last time.
    // accumulate statistics over the period of a second
    if (inCurrentTime - fBucketBegin > fSendInterval)
    {
        fPreviousBucketBegin = fBucketBegin;
        fBucketBegin = inCurrentTime;
		if (fPreviousBucketBegin == 0)
			fPreviousBucketBegin = fBucketBegin - fSendInterval;
        fBytesDuringBucket = 0;
        if (inCurrentTime - fLastSecondStart > 1000)
        {
            fBytesDuringPreviousSecond = fBytesDuringLastSecond;
            fBytesDuringLastSecond = 0;
            fPreviousSecondStart = fLastSecondStart;
            fLastSecondStart = inCurrentTime;
        }
        
        fPreviousBucketTimeAhead = fBucketTimeAhead;
    }
    
	if (fOverbufferWindowBegin == -1)
		fOverbufferWindowBegin = inCurrentTime;
	
	if ((inTransmitTime <= inCurrentTime + fSendInterval) || 
		(fOverbufferingEnabled && (inTransmitTime <= inCurrentTime + fSendInterval + fSendAheadDurationInMsec)))
    {
        //
        // If this happens, this packet needs to be sent regardless of overbuffering
        return -1;
    }
    
    if (!fOverbufferingEnabled || (fWindowSize == 0))
        return inTransmitTime;
        
    // if the client is running low on memory, wait a while for it to be freed up
    // there's nothing magic bout these numbers, we're just trying to be conservative
    if ((fWindowSize != -1) && (inPacketSize * 5 > fWindowSize - fBytesSentSinceLastReport))
    {
        return inCurrentTime + (fSendInterval * 5);  // client reports don't come that often
    }
    
    // if we're far enough ahead, then wait until it's time to send more packets
    if (inTransmitTime - inCurrentTime > fMaxSendAheadTime)
        return inTransmitTime - fMaxSendAheadTime + fSendInterval;
        
    // during the first second just send packets normally
//    if (fPreviousSecondStart == -1)
//        return inCurrentTime + fSendInterval;
    
    // now figure if we want to send this packet during this bucket.  We have two limitations.
    // First we scale up bitrate slowly, so we should only try and send a little more than we
    // sent recently (averaged over a second or two).  However, we always try and send at
    // least the current bitrate and never more than double.
//    SInt32 currentBitRate = fBytesDuringBucket * 1000 / (inCurrentTime - fPreviousBucketBegin);
//  SInt32 averageBitRate = (fBytesDuringPreviousSecond + fBytesDuringLastSecond) * 1000 / (inCurrentTime - fPreviousSecondStart);
//    SInt32 averageBitRate = fBytesDuringPreviousSecond * 1000 / (fLastSecondStart - fPreviousSecondStart);
	fBucketTimeAhead = inTransmitTime - inCurrentTime;
//	printf("Current br = %d, average br = %d (cta = %qd, pta = %qd)\n", currentBitRate, averageBitRate, currentTimeAhead, fPreviousBucketTimeAhead);
    
    // always try and stay as far ahead as we were before
	if (fBucketTimeAhead < fPreviousBucketTimeAhead)
        return -1;
        
    // but don't send at more that double the bitrate (for any given time we should only get further
    // ahead by that amount of time)
	//printf("cta - pta = %qd, ct - pbb = %qd\n", fBucketTimeAhead - fPreviousBucketTimeAhead, SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)));
	if (fBucketTimeAhead - fPreviousBucketTimeAhead > ((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)))
	{ 
		fBucketTimeAhead = fPreviousBucketTimeAhead + SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0));
		return inCurrentTime + fSendInterval;		// this will get us to the next bucket
	}
        
	// don't send more than 10% over the average bitrate for the previous second
//    if (currentBitRate > averageBitRate * 11 / 10)
//        return inCurrentTime + fSendInterval;       // this will get us to the next bucket
    
    return -1;  // send this packet
}

void RTPOverbufferWindow::ResetOverBufferWindow()
{
    fBytesDuringLastSecond = 0;
    fLastSecondStart = -1;
    fBytesDuringPreviousSecond = 0;
    fPreviousSecondStart = -1;
    fBytesDuringBucket = 0;
    fBucketBegin = 0;
    fBucketTimeAhead = 0;
    fPreviousBucketTimeAhead = 0;
	fOverbufferWindowBegin = -1;
}

void RTPOverbufferWindow::AddPacketToWindow(SInt32 inPacketSize)
{
    fBytesDuringBucket += inPacketSize;
    fBytesDuringLastSecond += inPacketSize;
    fBytesSentSinceLastReport += inPacketSize;
}

void RTPOverbufferWindow::EmptyOutWindow(const SInt64& inCurrentTime)
{
    // no longer needed
}

void RTPOverbufferWindow::SetWindowSize(UInt32 inWindowSizeInBytes)
{
    fWindowSize = inWindowSizeInBytes;
    fBytesSentSinceLastReport = 0;
}
