/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple 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:       RTPBandwidthTracker.h

    Contains:   Uses Karns Algorithm to measure round trip times. This also
                tracks the current window size based on input from the caller.
    
    ref:
    
    Improving Round-Trip Time Estimates in Reliable Transport Protocols, ACM SIGCOMM, ???
    
    Congestion Avoidance and Control - Van Jacobson, November 1988 -- Preoceedings of SIGCOMM '88

    Internetworking with TCP/IP Comer and Stevens, Chap 14, prentice hall 1991
*/

#ifndef __RTP_BANDWIDTH_TRACKER_H__
#define __RTP_BANDWIDTH_TRACKER_H__

#include "OSHeaders.h"

class RTPBandwidthTracker
{
    public:
        RTPBandwidthTracker(Bool16 inUseSlowStart)
         :  fRunningAverageMSecs(0),
            fRunningMeanDevationMSecs(0),
            fCurRetransmitTimeout( kMinRetransmitIntervalMSecs ),
            fUnadjustedRTO( kMinRetransmitIntervalMSecs ),
            fCongestionWindow(kMaximumSegmentSize),
            fSlowStartThreshold(0),
            fSlowStartByteCount(0),
            fClientWindow(0),
            fBytesInList(0),
            fAckTimeout(kMinAckTimeout),
            fUseSlowStart(inUseSlowStart),
            fMaxCongestionWindowSize(0),
            fMinCongestionWindowSize(1000000),
            fMaxRTO(0),
            fMinRTO(24000),
            fTotalCongestionWindowSize(0),
            fTotalRTO(0),
            fNumStatsSamples(0)
        {}
        
        ~RTPBandwidthTracker() {}
        
        //
        // Initialization - give the client's window size.
        void    SetWindowSize(SInt32 clientWindowSize);

        //
        // Each RTT sample you get, let the tracker know what it is
        // so it can keep a good running average.
        void AddToRTTEstimate( SInt32 rttSampleMSecs );
        
        //
        // Before sending new data, let the tracker know
        // how much data you are sending so it can adjust the window.
        void FillWindow(UInt32 inNumBytes)
            { fBytesInList += inNumBytes; fIsRetransmitting = false; }
        
        //
        // When data is acked, let the tracker know how much
        // data was acked so it can adjust the window
        void EmptyWindow(UInt32 inNumBytes, Bool16 updateBytesInList = true);
        
        //
        // When retransmitting a packet, call this function so
        // the tracker can adjust the window sizes and back off.
        void AdjustWindowForRetransmit();
        
        //
        // ACCESSORS
        const Bool16 ReadyForAckProcessing()    { return (fClientWindow > 0 && fCongestionWindow > 0); } // see RTPBandwidthTracker::EmptyWindow for requirements
        const Bool16 IsFlowControlled()         { return ( (SInt32)fBytesInList >= fCongestionWindow ); }
        const SInt32 ClientWindowSize()         { return fClientWindow; }
        const UInt32 BytesInList()              { return fBytesInList; }
        const SInt32 CongestionWindow()         { return fCongestionWindow; }
        const SInt32 SlowStartThreshold()       { return fSlowStartThreshold; }
        const SInt32 RunningAverageMSecs()      { return fRunningAverageMSecs / 8; }  // fRunningAverageMSecs is stored scaled up 8x
        const SInt32 RunningMeanDevationMSecs() { return fRunningMeanDevationMSecs/ 4; } // fRunningMeanDevationMSecs is stored scaled up 4x
        const SInt32 CurRetransmitTimeout()     { return fCurRetransmitTimeout; }
        const SInt32 GetCurrentBandwidthInBps()
            { return (fUnadjustedRTO > 0) ? (fCongestionWindow * 1000) / fUnadjustedRTO : 0; }
        inline const UInt32 RecommendedClientAckTimeout() { return fAckTimeout; }
        void UpdateAckTimeout(UInt32 bitsSentInInterval, SInt64 intervalLengthInMsec);
        void UpdateStats();

        //
        // Stats
        SInt32              GetMaxCongestionWindowSize()    { return fMaxCongestionWindowSize; }
        SInt32              GetMinCongestionWindowSize()    { return fMinCongestionWindowSize; }
        SInt32              GetAvgCongestionWindowSize()    { return (SInt32)(fTotalCongestionWindowSize / (SInt64)fNumStatsSamples); }
        SInt32              GetMaxRTO()                     { return fMaxRTO; }
        SInt32              GetMinRTO()                     { return fMinRTO; }
        SInt32              GetAvgRTO()                     { return (SInt32)(fTotalRTO / (SInt64)fNumStatsSamples); }
        
        enum
        {
            kMaximumSegmentSize = 1466,  // enet - just a guess!
            
            //
            // Our algorithm for telling the client what the ack timeout
            // is currently not too sophisticated. This could probably be made
            // better. During slow start, we just use 20, and afterwards, just use 100
            kMinAckTimeout = 20,
            kMaxAckTimeout = 100
        };
        
    private:
    
        //
        // For computing the round-trip estimate using Karn's algorithm
        SInt32  fRunningAverageMSecs;
        SInt32  fRunningMeanDevationMSecs;
        SInt32  fCurRetransmitTimeout;
        SInt32  fUnadjustedRTO;
        
        //
        // Tracking our window sizes
        SInt64              fLastCongestionAdjust;
        SInt32              fCongestionWindow;      // implentation of VJ congestion avoidance
        SInt32              fSlowStartThreshold;    // point at which we stop adding to the window for each ack, and add to the window for each window full of acks
        SInt32              fSlowStartByteCount;            // counts window a full of acks when past ss thresh
        SInt32              fClientWindow;          // max window size based on client UDP buffer
        UInt32              fBytesInList;               // how many unacked bytes on this stream
        UInt32              fAckTimeout;
        
        Bool16              fUseSlowStart;
        Bool16              fIsRetransmitting;      // are we in the re-transmit 'state' ( started resending, but have yet to send 'new' data

        //
        // Stats
        SInt32              fMaxCongestionWindowSize;
        SInt32              fMinCongestionWindowSize;
        SInt32              fMaxRTO;
        SInt32              fMinRTO;
        SInt64              fTotalCongestionWindowSize;
        SInt64              fTotalRTO;
        SInt32              fNumStatsSamples;
        
        enum
        {
            kMinRetransmitIntervalMSecs = 600,
            kMaxRetransmitIntervalMSecs = 24000
        };
};

#endif // __RTP_BANDWIDTH_TRACKER_H__
