//this file is part of eMule
//Copyright (C)2002-2006 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
//
//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., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include <math.h>
#include <Mmsystem.h>
#include "emule.h"
#include "UploadBandwidthThrottler.h"
#include "EMSocket.h"
#include "opcodes.h"
#include "LastCommonRouteFinder.h"
#include "OtherFunctions.h"
#include "emuledlg.h"
#include "uploadqueue.h"
#include "preferences.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#endif


/**
 * The constructor starts the thread.
 */
UploadBandwidthThrottler::UploadBandwidthThrottler(void) {
    m_SentBytesSinceLastCall = 0;
	m_SentBytesSinceLastCallOverhead = 0;
    //MORPH - Changed by SiRoB, Upload Splitting Class
	/*
	m_highestNumberOfFullyActivatedSlots = 0;
	*/
	for (uint8 i = 0; i < NB_SPLITTING_CLASS; i++)
		m_highestNumberOfFullyActivatedSlots[i] = (UINT)0;
	memset(slotCounterClass,0,sizeof(slotCounterClass));
		
	threadEndedEvent = new CEvent(0, 1);
    pauseEvent = new CEvent(TRUE, TRUE);

    doRun = true;
    AfxBeginThread(RunProc, (LPVOID)this);
}

/**
 * The destructor stops the thread. If the thread has already stoppped, destructor does nothing.
 */
UploadBandwidthThrottler::~UploadBandwidthThrottler(void) {
    EndThread();
    delete threadEndedEvent;
	delete pauseEvent;
}

/**
 * Find out how many bytes that has been put on the sockets since the last call to this
 * method. Includes overhead of control packets.
 *
 * @return the number of bytes that has been put on the sockets since the last call
 */
uint64 UploadBandwidthThrottler::GetNumberOfSentBytesSinceLastCallAndReset() {
    sendLocker.Lock();
    
    uint64 numberOfSentBytesSinceLastCall = m_SentBytesSinceLastCall;
    m_SentBytesSinceLastCall = 0;

    sendLocker.Unlock();

    return numberOfSentBytesSinceLastCall;
}

/**
 * Find out how many bytes that has been put on the sockets since the last call to this
 * method. Excludes overhead of control packets.
 *
 * @return the number of bytes that has been put on the sockets since the last call
 */
uint64 UploadBandwidthThrottler::GetNumberOfSentBytesOverheadSinceLastCallAndReset() {
    sendLocker.Lock();
    
	uint64 numberOfSentBytesSinceLastCall = m_SentBytesSinceLastCallOverhead;
	m_SentBytesSinceLastCallOverhead = 0;

    sendLocker.Unlock();

    return numberOfSentBytesSinceLastCall;
}

/**
 * Find out the highest number of slots that has been fed data in the normal standard loop
 * of the thread since the last call of this method. This means all slots that haven't
 * been in the trickle state during the entire time since the last call.
 *
 * @return the highest number of fully activated slots during any loop since last call
 */
uint32 UploadBandwidthThrottler::GetHighestNumberOfFullyActivatedSlotsSinceLastCallAndReset(uint32 * HighestNumberOfFullyActivatedSlotsSinceLastCallClass) {
    sendLocker.Lock();
    
    //if(m_highestNumberOfFullyActivatedSlots > (uint32)m_StandardOrder_list.GetSize()) {
    //    theApp.QueueDebugLogLine(true, _T("UploadBandwidthThrottler: Throttler wants new slot when get-method called. m_highestNumberOfFullyActivatedSlots: %i m_StandardOrder_list.GetSize(): %i tick: %i"), m_highestNumberOfFullyActivatedSlots, m_StandardOrder_list.GetSize(), ::GetTickCount());
    //}

    uint32 highestNumberOfFullyActivatedSlots = m_highestNumberOfFullyActivatedSlots[LAST_CLASS];
	*HighestNumberOfFullyActivatedSlotsSinceLastCallClass = m_highestNumberOfFullyActivatedSlots[0];
	*++HighestNumberOfFullyActivatedSlotsSinceLastCallClass = m_highestNumberOfFullyActivatedSlots[1];
	*++HighestNumberOfFullyActivatedSlotsSinceLastCallClass = m_highestNumberOfFullyActivatedSlots[LAST_CLASS];
	for (uint8 i = 0; i < NB_SPLITTING_CLASS; i++)
		m_highestNumberOfFullyActivatedSlots[i] = (UINT)0;
	sendLocker.Unlock();

    return highestNumberOfFullyActivatedSlots;
}

/**
 * Add a socket to the list of sockets that have upload slots. The main thread will
 * continously call send on these sockets, to give them chance to work off their queues.
 * The sockets are called in the order they exist in the list, so the top socket (index 0)
 * will be given a chance first to use bandwidth, and then the next socket (index 1) etc.
 *
 * It is possible to add a socket several times to the list without removing it inbetween,
 * but that should be avoided.
 *
 * @param index insert the socket at this place in the list. An index that is higher than the
 *              current number of sockets in the list will mean that the socket should be inserted
 *              last in the list.
 *
 * @param socket the address to the socket that should be added to the list. If the address is NULL,
 *               this method will do nothing.
 */
void UploadBandwidthThrottler::AddToStandardList(uint32 index, ThrottledFileSocket* socket, uint32 classID, bool scheduled) {
    if(socket != NULL) {
        sendLocker.Lock();

		RemoveFromStandardListNoLock(socket);
		if(index > (uint32)m_StandardOrder_list.GetSize()) {
			index = m_StandardOrder_list.GetSize();
		}
		m_StandardOrder_list.InsertAt(index, socket);
		//MORPH START - Added by SiRoB & AndCycle, Upload Splitting Class
		Socket_stat* cur_socket_stat = NULL;
		if (!m_stat_list.Lookup(socket,cur_socket_stat)){
			cur_socket_stat = new Socket_stat;
			m_stat_list.SetAt(socket,cur_socket_stat);
			cur_socket_stat->realBytesToSpend = 999;
		}
		cur_socket_stat->scheduled = scheduled;
		if (scheduled)
			++slotCounterClass[NB_SCHED_CLASS];
		cur_socket_stat->classID = classID;
		++slotCounterClass[classID];
		/*
		for (uint32 i = 0; i < NB_SPLITTING_CLASS;i++)
			m_highestNumberOfFullyActivatedSlots[i] = 0;
		*/
		//MORPH END - Added by SiRoB & AndCycle, Upload Splitting Class

        sendLocker.Unlock();
//	} else {
//		if (thePrefs.GetVerbose())
//		DebugLogError(LOG_STATUSBAR,_T("Tried to add NULL socket to UploadBandwidthThrottler Standard list! Prevented."));
    }
}

/**
 * Remove a socket from the list of sockets that have upload slots.
 *
 * If the socket has mistakenly been added several times to the list, this method
 * will return all of the entries for the socket.
 *
 * @param socket the address of the socket that should be removed from the list. If this socket
 *               does not exist in the list, this method will do nothing.
 */
bool UploadBandwidthThrottler::RemoveFromStandardList(ThrottledFileSocket* socket, bool resort) {
	bool returnValue;
    sendLocker.Lock();

	//MORPH START - Added by SiRoB, Upload Splitting Class
	/*
	returnValue = RemoveFromStandardListNoLock(socket);
	*/
	returnValue = RemoveFromStandardListNoLock(socket, resort);
	//MORPH END   - Added by SiRoB, Upload Splitting Class
	
    sendLocker.Unlock();

    return returnValue;
}

/**
 * Remove a socket from the list of sockets that have upload slots. NOT THREADSAFE!
 * This is an internal method that doesn't take the necessary lock before it removes
 * the socket. This method should only be called when the current thread already owns
 * the sendLocker lock!
 *
 * @param socket address of the socket that should be removed from the list. If this socket
 *               does not exist in the list, this method will do nothing.
 */
bool UploadBandwidthThrottler::RemoveFromStandardListNoLock(ThrottledFileSocket* socket, bool resort) {
	// Find the slot
    int slotCounter = 0;
    bool foundSocket = false;
	uint32 classID = NULL;
	while(slotCounter < m_StandardOrder_list.GetSize() && foundSocket == false) {
        if(m_StandardOrder_list.GetAt(slotCounter) == socket) {
			// Remove the slot
            m_StandardOrder_list.RemoveAt(slotCounter);
			// Remove the slot
			Socket_stat* stat = NULL;
			m_stat_list.Lookup(socket,stat);
			classID = stat->classID;
			if (stat->scheduled)
				--slotCounterClass[NB_SCHED_CLASS];
			if (!resort) {
				delete stat;
				m_stat_list.RemoveKey(socket);
			}
			--slotCounterClass[classID];
			foundSocket = true;
        } else {
            slotCounter++;
        }
    }

	//MORPH START - Added by SiRoB, Upload Splitting Class
	if(resort == false && foundSocket) {
		for (uint32 i = 0; i < NB_SPLITTING_CLASS;i++)
			if (m_highestNumberOfFullyActivatedSlots[i] > slotCounterClass[classID])
				m_highestNumberOfFullyActivatedSlots[i] = slotCounterClass[classID];
    }
	//MORPH END  - Added by SiRoB, Upload Splitting Class

	return foundSocket;
}

/**
* Notifies the send thread that it should try to call controlpacket send
* for the supplied socket. It is allowed to call this method several times
* for the same socket, without having controlpacket send called for the socket
* first. The doublette entries are never filtered, since it is incurs less cpu
* overhead to simply call Send() in the socket for each double. Send() will
* already have done its work when the second Send() is called, and will just
* return with little cpu overhead.
*
* @param socket address to the socket that requests to have controlpacket send
*               to be called on it
*/
void UploadBandwidthThrottler::QueueForSendingControlPacket(ThrottledControlSocket* socket, bool hasSent) {
	// Get critical section
    tempQueueLocker.Lock();

    if(doRun) {
        if(hasSent) {
            m_TempControlQueueFirst_list.AddTail(socket);
        } else {
        	m_TempControlQueue_list.AddTail(socket);
    	}
    }

	// End critical section
    tempQueueLocker.Unlock();
}

/**
 * Remove the socket from all lists and queues. This will make it safe to
 * erase/delete the socket. It will also cause the main thread to stop calling
 * send() for the socket.
 *
 * @param socket address to the socket that should be removed
 */
void UploadBandwidthThrottler::RemoveFromAllQueues(ThrottledControlSocket* socket, bool lock) {
    if(lock) {
		// Get critical section
   		sendLocker.Lock();
    }

    if(doRun) {
        // Remove this socket from control packet queue
        {
            POSITION pos1, pos2;
	        for (pos1 = m_ControlQueue_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_ControlQueue_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_ControlQueue_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_ControlQueue_list.RemoveAt(pos2);
                }
            }
        }
        
        {
            POSITION pos1, pos2;
	        for (pos1 = m_ControlQueueFirst_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_ControlQueueFirst_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_ControlQueueFirst_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_ControlQueueFirst_list.RemoveAt(pos2);
                }
            }
        }

        tempQueueLocker.Lock();
        {
            POSITION pos1, pos2;
	        for (pos1 = m_TempControlQueue_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_TempControlQueue_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_TempControlQueue_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_TempControlQueue_list.RemoveAt(pos2);
                }
            }
        }

        {
            POSITION pos1, pos2;
	        for (pos1 = m_TempControlQueueFirst_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_TempControlQueueFirst_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_TempControlQueueFirst_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_TempControlQueueFirst_list.RemoveAt(pos2);
                }
            }
        }
        tempQueueLocker.Unlock();
    }

    if(lock) {
		// End critical section
        sendLocker.Unlock();
    }
}

void UploadBandwidthThrottler::RemoveFromAllQueues(ThrottledFileSocket* socket) {
	// Get critical section
    sendLocker.Lock();

    if(doRun) {
        RemoveFromAllQueues(socket, false);

		// And remove it from upload slots
        RemoveFromStandardListNoLock(socket);
    }

	// End critical section
    sendLocker.Unlock();
}

/**
 * Make the thread exit. This method will not return until the thread has stopped
 * looping. This guarantees that the thread will not access the CEMSockets after this
 * call has exited.
 */
void UploadBandwidthThrottler::EndThread() {
    sendLocker.Lock();

	// signal the thread to stop looping and exit.
    doRun = false;

    sendLocker.Unlock();

    Pause(false);

	// wait for the thread to signal that it has stopped looping.
    threadEndedEvent->Lock();
}

void UploadBandwidthThrottler::Pause(bool paused) {
    if(paused) {
        pauseEvent->ResetEvent();
    } else {
        pauseEvent->SetEvent();
    }
}

uint32 UploadBandwidthThrottler::GetSlotLimit(uint32 currentUpSpeed) {
    uint32 upPerClient = UPLOAD_CLIENT_DATARATE;

    // if throttler doesn't require another slot, go with a slightly more restrictive method
	if( currentUpSpeed > 20*1024 )
		upPerClient += currentUpSpeed/43;

	if( upPerClient > 7680 )
		upPerClient = 7680;

	//now the final check

	uint16 nMaxSlots;
	if (currentUpSpeed > 12*1024)
		nMaxSlots = (uint16)(((float)currentUpSpeed) / upPerClient);
	else if (currentUpSpeed > 7*1024)
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED + 2;
	else if (currentUpSpeed > 3*1024)
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED + 1;
	else
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED;

    return max(nMaxSlots, MIN_UP_CLIENTS_ALLOWED);
}

uint32 UploadBandwidthThrottler::CalculateChangeDelta(uint32 numberOfConsecutiveChanges) const {
    switch(numberOfConsecutiveChanges) {
        case 0: return 50;
        case 1: return 50;
        case 2: return 128;
        case 3: return 256;
        case 4: return 512;
        case 5: return 512+256;
        case 6: return 1*1024;
        case 7: return 1*1024+256;
        default: return 1*1024+512;
    }
}

/**
 * Start the thread. Called from the constructor in this class.
 *
 * @param pParam
 *
 * @return
 */
UINT AFX_CDECL UploadBandwidthThrottler::RunProc(LPVOID pParam) {
	DbgSetThreadName("UploadBandwidthThrottler");

	// SLUGFILLER: SafeHash
	CReadWriteLock lock(&theApp.m_threadlock);
	if (!lock.ReadLock(0))
		return 0;
	// SLUGFILLER: SafeHash

	InitThreadLocale();

    UploadBandwidthThrottler* uploadBandwidthThrottler = (UploadBandwidthThrottler*)pParam;

    return uploadBandwidthThrottler->RunInternal();
}

/**
 * The thread method that handles calling send for the individual sockets.
 *
 * Control packets will always be tried to be sent first. If there is any bandwidth leftover
 * after that, send() for the upload slot sockets will be called in priority order until we have run
 * out of available bandwidth for this loop. Upload slots will not be allowed to go without having sent
 * called for more than a defined amount of time (i.e. two seconds).
 *
 * @return always returns 0.
 */
UINT UploadBandwidthThrottler::RunInternal() {
	DWORD lastLoopTick = timeGetTime();
	//MORPH START - Added by SiRoB, Upload Splitting Class
	DWORD lastLoopTickTryTosend = lastLoopTick;
	uint32 allowedDataRateClass[NB_SPLITTING_CLASS];
	uint32 ClientDataRate[NB_SPLITTING_CLASS];
	sint64 realBytesToSpendClass[NB_SPLITTING_CLASS];
	memset(realBytesToSpendClass,0,sizeof(realBytesToSpendClass));
	uint32 curAllowedTrickle[NB_SPLITTING_CLASS];
	memset(curAllowedTrickle,0,sizeof(curAllowedTrickle));
	//MORPH END   - Added by SiRoB, Upload Splitting Class
	uint32 nEstiminatedLimit = 0;
	int nSlotsBusyLevel = 0;
	DWORD nUploadStartTime = 0;
    uint32 numberOfConsecutiveUpChanges = 0;
    uint32 numberOfConsecutiveDownChanges = 0;
    uint32 changesCount = 0;
    uint32 loopsCount = 0;

    bool estimateChangedLog = false;
    bool lotsOfLog = false;

    while(doRun) {
        pauseEvent->Lock();

		theApp.lastCommonRouteFinder->GetClassByteToSend(allowedDataRateClass,ClientDataRate);

	// check busy level for all the slots (WSAEWOULDBLOCK status)
        uint32 cBusy = 0;
        uint32 nCanSend = 0;

        sendLocker.Lock();
        for (int i = 0; i < m_StandardOrder_list.GetSize() && (i < 3 || (UINT)i < GetSlotLimit(theApp.uploadqueue->GetDatarate())); i++){
            if (m_StandardOrder_list[i] != NULL && m_StandardOrder_list[i]->HasQueues()) {
                nCanSend++;

                if(m_StandardOrder_list[i]->IsBusy())
					cBusy++;
            }
		}
        sendLocker.Unlock();

        // if this is kept, the loop above can be a little optimized (don't count nCanSend, just use nCanSend = GetSlotLimit(theApp.uploadqueue->GetDatarate())
        if(theApp.uploadqueue)
            nCanSend = max(nCanSend, GetSlotLimit(theApp.uploadqueue->GetDatarate()));

        // When no upload limit has been set in options, try to guess a good upload limit.
		bool bUploadUnlimited = (thePrefs.GetMaxUpload() == UNLIMITED);
        if (bUploadUnlimited) {
            loopsCount++;

            //if(lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: busy: %i/%i nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), cBusy, nCanSend, nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

            if(nCanSend > 0) {
			    float fBusyPercent = ((float)cBusy/(float)nCanSend) * 100;
                if (cBusy > 2 && fBusyPercent > 75.00f && nSlotsBusyLevel < 255){
				    nSlotsBusyLevel++;
                    changesCount++;
                    if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
			    }
			    else if ( (cBusy <= 2 || fBusyPercent < 25.00f) && nSlotsBusyLevel > (-255)){
				    nSlotsBusyLevel--;
                    changesCount++;
                    if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
                }
			}

            if(nUploadStartTime == 0) {
		        if (m_StandardOrder_list.GetSize() >= 3)
			        nUploadStartTime = timeGetTime();
            } else if(timeGetTime()- nUploadStartTime > SEC2MS(60)) {
			    if (theApp.uploadqueue){
				    if (nEstiminatedLimit == 0){ // no autolimit was set yet
					    if (nSlotsBusyLevel >= 250){ // sockets indicated that the BW limit has been reached
						    nEstiminatedLimit = theApp.uploadqueue->GetDatarate();
						    allowedDataRateClass[LAST_CLASS] = min(nEstiminatedLimit, allowedDataRateClass[LAST_CLASS]);
						    nSlotsBusyLevel = -200;
                            if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: Set inital estimated limit to %0.5f changesCount: %i loopsCount: %i"), (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
                            changesCount = 0;
                            loopsCount = 0;
					    }
				    }
				    else{
                        if (nSlotsBusyLevel > 250){
                            if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) {
                                numberOfConsecutiveDownChanges = 0;
                            }
                            numberOfConsecutiveDownChanges++;
                            uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveDownChanges);

                            // Don't lower speed below 1 KBytes/s
                            if(nEstiminatedLimit < changeDelta + 1024) {
                                if(nEstiminatedLimit > 1024) {
                                    changeDelta = nEstiminatedLimit - 1024;
                                } else {
                                    changeDelta = 0;
                                }
                            }
                            ASSERT(nEstiminatedLimit >= changeDelta + 1024);
    						nEstiminatedLimit -= changeDelta;

                            if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: REDUCED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveDownChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

                            numberOfConsecutiveUpChanges = 0;
						    nSlotsBusyLevel = 0;
                            changesCount = 0;
                            loopsCount = 0;
					    }
                        else if (nSlotsBusyLevel < (-250)){
                            if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) {
                                numberOfConsecutiveUpChanges = 0;
                            }
                            numberOfConsecutiveUpChanges++;
                            uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveUpChanges);

                            // Don't raise speed unless we are under current allowedDataRate
                            if(nEstiminatedLimit+changeDelta > allowedDataRateClass[LAST_CLASS]) {
                                if(nEstiminatedLimit < allowedDataRateClass[LAST_CLASS]) {
                                    changeDelta = allowedDataRateClass[LAST_CLASS] - nEstiminatedLimit;
                                } else {
                                    changeDelta = 0;
                                }
                            }
                            ASSERT(nEstiminatedLimit < allowedDataRateClass[LAST_CLASS] && nEstiminatedLimit+changeDelta <= allowedDataRateClass[LAST_CLASS] || nEstiminatedLimit >= allowedDataRateClass[LAST_CLASS] && changeDelta == 0);
                            nEstiminatedLimit += changeDelta;

                            if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: INCREASED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveUpChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

                            numberOfConsecutiveDownChanges = 0;
						    nSlotsBusyLevel = 0;
                            changesCount = 0;
                            loopsCount = 0;
					    }

					    allowedDataRateClass[LAST_CLASS] = min(nEstiminatedLimit, allowedDataRateClass[LAST_CLASS]);
				    } 
			    }
            }
		}

        /*if(cBusy == nCanSend && m_StandardOrder_list.GetSize() > 0) {
            allowedDataRateClass[LAST_CLASS] = 0;
            if(nSlotsBusyLevel < 125 && bUploadUnlimited) {
                nSlotsBusyLevel = 125;
                if(thePrefs.GetVerbose() && lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i (set due to all slots busy)"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
            }
        }*/

		uint32 minFragSize = 1300;
		uint32 doubleSendSize = minFragSize*2; // send two packages at a time so they can share an ACK
		if(allowedDataRateClass[LAST_CLASS] < 6*1024) {
			minFragSize = 536;
			doubleSendSize = minFragSize; // don't send two packages at a time at very low speeds to give them a smoother load
		}
		#define TIME_BETWEEN_UPLOAD_LOOPS 1
        uint32 sleepTime;
        if(allowedDataRateClass[LAST_CLASS] == _UI32_MAX || realBytesToSpendClass[LAST_CLASS] >= 1000 || allowedDataRateClass[LAST_CLASS] == 0 && nEstiminatedLimit == 0) {
            // we could send at once, but sleep a while to not suck up all cpu
            sleepTime = TIME_BETWEEN_UPLOAD_LOOPS;
        } else if(allowedDataRateClass[LAST_CLASS] == 0) {
            sleepTime = max((uint32)ceil((double)1000*doubleSendSize/nEstiminatedLimit), TIME_BETWEEN_UPLOAD_LOOPS);
        } else {
            // sleep for just as long as we need to get back to having one byte to send
            sleepTime = max((uint32)ceil((double)(-realBytesToSpendClass[LAST_CLASS] + 1000)/allowedDataRateClass[LAST_CLASS]), TIME_BETWEEN_UPLOAD_LOOPS);
        }
		DWORD timeSinceLastLoop = timeGetTime() - lastLoopTick;
		if(timeSinceLastLoop < sleepTime)
            Sleep(sleepTime-timeSinceLastLoop);
		const DWORD thisLoopTick = timeGetTime();
        if (thisLoopTick - lastLoopTick > 2000) {
			lastLoopTick = thisLoopTick;
        }
		timeSinceLastLoop = thisLoopTick - lastLoopTick;

		lastLoopTick = thisLoopTick;

		if (timeSinceLastLoop > 0) {
			for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
				uint32 allowedDataRate = allowedDataRateClass[classID];
				if(allowedDataRate > 0 && allowedDataRate != _UI32_MAX) {
					if (_I64_MAX/timeSinceLastLoop > allowedDataRate && _I64_MAX-allowedDataRate*timeSinceLastLoop > realBytesToSpendClass[classID]) {
						realBytesToSpendClass[classID] += allowedDataRate*timeSinceLastLoop;
					} else {
						realBytesToSpendClass[classID] = _I64_MAX;
					}
				} else {
					if (classID < LAST_CLASS)
						realBytesToSpendClass[classID] = _I64_MAX;
					else
						realBytesToSpendClass[LAST_CLASS] = 1000;
				}
			}
		}
	
		sint64 BytesToSpend = realBytesToSpendClass[LAST_CLASS] / 1000;
		if(BytesToSpend >= 1 || allowedDataRateClass[LAST_CLASS] == 0) {
			if (thisLoopTick - lastLoopTickTryTosend > 2000)
				lastLoopTickTryTosend = thisLoopTick;
			timeSinceLastLoop = thisLoopTick - lastLoopTickTryTosend;
			lastLoopTickTryTosend = thisLoopTick;

			sendLocker.Lock();
	    		tempQueueLocker.Lock();

			// are there any sockets in m_TempControlQueue_list? Move them to normal m_ControlQueue_list;
			while(!m_TempControlQueueFirst_list.IsEmpty()) {
				ThrottledControlSocket* moveSocket = m_TempControlQueueFirst_list.RemoveHead();
				m_ControlQueueFirst_list.AddTail(moveSocket);
			}
			while(!m_TempControlQueue_list.IsEmpty()) {
				ThrottledControlSocket* moveSocket = m_TempControlQueue_list.RemoveHead();
       			m_ControlQueue_list.AddTail(moveSocket);
			}
   		tempQueueLocker.Unlock();

	    
		uint64 ControlspentBytes = 0;
		uint64 ControlspentOverhead = 0;

			// Send any queued up control packets first
		while((BytesToSpend > 0 && ControlspentBytes < (uint64)BytesToSpend|| allowedDataRateClass[LAST_CLASS] == 0 && ControlspentBytes < 500) && (!m_ControlQueueFirst_list.IsEmpty() || !m_ControlQueue_list.IsEmpty())) {
				ThrottledControlSocket* socket = NULL;
					
				if(!m_ControlQueueFirst_list.IsEmpty()) {
					socket = m_ControlQueueFirst_list.RemoveHead();
				} else if(!m_ControlQueue_list.IsEmpty()) {
					socket = m_ControlQueue_list.RemoveHead();
				}

				if (socket != NULL) {
						SocketSentBytes socketSentBytes = socket->SendControlData(allowedDataRateClass[LAST_CLASS] > 0?(UINT)(BytesToSpend - ControlspentBytes):1, minFragSize);
					uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
					if (lastSpentBytes) {
					Socket_stat* stat = NULL;
					if (m_stat_list.Lookup((ThrottledFileSocket*)socket, stat)) {
							stat->realBytesToSpend -= lastSpentBytes*1000;
							if (stat->classID < LAST_CLASS)
								realBytesToSpendClass[stat->classID] -= lastSpentBytes*1000;
					}
					ControlspentBytes += lastSpentBytes;
					ControlspentOverhead += socketSentBytes.sentBytesControlPackets;
					}
       			}
			}

			realBytesToSpendClass[LAST_CLASS] -= 1000*ControlspentBytes;
			m_SentBytesSinceLastCall += ControlspentBytes;
			m_SentBytesSinceLastCallOverhead += ControlspentOverhead;
			uint32 lastclientpos = 0;
			for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
				if (slotCounterClass[classID]) {
					ControlspentBytes = 0;
					ControlspentOverhead = 0;
					BytesToSpend = realBytesToSpendClass[LAST_CLASS] / 1000;
					if(classID < LAST_CLASS) {
						sint64 curClassByteToSpend = realBytesToSpendClass[classID] / 1000;
					if (BytesToSpend > 0 && BytesToSpend > curClassByteToSpend) {
						BytesToSpend = curClassByteToSpend;
						}
					}
					for(uint32 slotCounter = lastclientpos; slotCounter < lastclientpos + slotCounterClass[classID]; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket, stat)) {
								if (timeSinceLastLoop > 0) {
									uint32 allowedclientdatarate = _UI32_MAX;
									if (allowedDataRateClass[LAST_CLASS])	
										allowedclientdatarate = allowedDataRateClass[LAST_CLASS]; 
									if (allowedDataRateClass[classID])
										allowedclientdatarate = min(allowedclientdatarate,allowedDataRateClass[classID]); 
									if (ClientDataRate[classID])
										allowedclientdatarate = min(allowedclientdatarate,ClientDataRate[classID]);
									sint64 limit = -((sint64)2000*allowedclientdatarate);
									if (stat->realBytesToSpend < limit)
										stat->realBytesToSpend = limit;
									limit = (sint64)1000*doubleSendSize;
									if (stat->realBytesToSpend >= limit)
										stat->realBytesToSpend = limit;
									
									if (_I64_MAX/timeSinceLastLoop > allowedclientdatarate && _I64_MAX-allowedclientdatarate*timeSinceLastLoop > stat->realBytesToSpend)
										stat->realBytesToSpend += allowedclientdatarate*timeSinceLastLoop;
									else
										stat->realBytesToSpend = _I64_MAX;
								}
								if(BytesToSpend > 0 && ControlspentBytes < (uint64)BytesToSpend) {
									// trickle
					   				uint32 neededBytes = socket->GetNeededBytes(1000 > minFragSize);
	    
									if (neededBytes > 0 && thisLoopTick-socket->GetLastCalledSend()>1000) {
										neededBytes = (uint32)min(BytesToSpend - ControlspentBytes, neededBytes?neededBytes:1);
										SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize);
										uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
										if (lastSpentBytes) {
											uint64 realByteSpent = lastSpentBytes*1000;
											stat->realBytesToSpend -= realByteSpent;
											if(m_highestNumberOfFullyActivatedSlots[classID] > slotCounter+1)
												m_highestNumberOfFullyActivatedSlots[classID] = slotCounter+1;
											if (m_highestNumberOfFullyActivatedSlots[LAST_CLASS] < m_highestNumberOfFullyActivatedSlots[classID])
												m_highestNumberOfFullyActivatedSlots[LAST_CLASS] = m_highestNumberOfFullyActivatedSlots[classID];
										if (classID < LAST_CLASS)
												realBytesToSpendClass[classID] -= realByteSpent;
											ControlspentBytes += lastSpentBytes;
											ControlspentOverhead += socketSentBytes.sentBytesControlPackets;
										}
									}
								}
							}
						}
					}
					lastclientpos += slotCounterClass[classID];
					realBytesToSpendClass[LAST_CLASS] -= 1000*ControlspentBytes;
					m_SentBytesSinceLastCall += ControlspentBytes;
					m_SentBytesSinceLastCallOverhead += ControlspentOverhead;
				}
			}

			lastclientpos = 0;
			for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
				if(slotCounterClass[classID]) {
					//Calculate allowed data to spend for a class (realBytesToSpendClass)
					BytesToSpend = realBytesToSpendClass[LAST_CLASS] / 1000;
					if(classID < LAST_CLASS) {
						sint64 curClassByteToSpend = realBytesToSpendClass[classID] / 1000;
					if (BytesToSpend > 0 && BytesToSpend > curClassByteToSpend) {
						BytesToSpend = curClassByteToSpend;
						}
					}
					uint64 spentBytes = 0;
					uint64 spentOverhead = 0;
					for(uint32 slotCounter = lastclientpos; slotCounter < lastclientpos + slotCounterClass[classID]; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket,stat)) {
								//Try to send client allowed data for a client but not more than class allowed data
								if (BytesToSpend > 0 && spentBytes < (uint64)BytesToSpend) {
									if (stat->realBytesToSpend > 999) {
										uint32 BytesToSend = min(ClientDataRate[classID]?ClientDataRate[classID]:allowedDataRateClass[LAST_CLASS]/slotCounterClass[classID], (allowedDataRateClass[classID]?allowedDataRateClass[classID]:allowedDataRateClass[LAST_CLASS])/slotCounterClass[classID]);
										BytesToSend = max(BytesToSend, doubleSendSize);
										BytesToSend = min(BytesToSend, (UINT)(BytesToSpend - spentBytes));
										SocketSentBytes socketSentBytes = socket->SendFileAndControlData(BytesToSend, doubleSendSize);
										uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
										if (lastSpentBytes) {
											stat->realBytesToSpend -= lastSpentBytes*1000;
											uint32 schedoff = slotCounter+1;
											if (lastSpentBytes >= doubleSendSize && (stat->scheduled || schedoff == lastclientpos+slotCounterClass[classID]))
												schedoff = lastclientpos+slotCounterClass[classID]+1;
											if(schedoff > m_highestNumberOfFullyActivatedSlots[classID] && (lastSpentBytes >= doubleSendSize)) // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/))
												m_highestNumberOfFullyActivatedSlots[classID] = schedoff;
											if (m_highestNumberOfFullyActivatedSlots[classID] > m_highestNumberOfFullyActivatedSlots[LAST_CLASS])
												m_highestNumberOfFullyActivatedSlots[LAST_CLASS] = m_highestNumberOfFullyActivatedSlots[classID];
											spentBytes += lastSpentBytes;
											spentOverhead += socketSentBytes.sentBytesControlPackets;
										}
									}
								}
							}
						}
					}
					for(uint32 slotCounter = lastclientpos; slotCounter < lastclientpos + slotCounterClass[classID]; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket,stat)) {
								if (BytesToSpend > 0 && spentBytes < (uint64)BytesToSpend) {
									if (stat->realBytesToSpend > 999) {
										SocketSentBytes socketSentBytes = socket->SendFileAndControlData((UINT)(BytesToSpend - spentBytes), doubleSendSize);
										uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
										if (lastSpentBytes) {
											stat->realBytesToSpend -= lastSpentBytes*1000;
											uint32 schedoff = slotCounter+1;
											if (lastSpentBytes >= doubleSendSize && (stat->scheduled || schedoff == lastclientpos+slotCounterClass[classID]))
												schedoff = lastclientpos+slotCounterClass[classID]+1;
											if(schedoff > m_highestNumberOfFullyActivatedSlots[classID] && (lastSpentBytes >= doubleSendSize)) // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/))
												m_highestNumberOfFullyActivatedSlots[classID] = schedoff;
											if (m_highestNumberOfFullyActivatedSlots[classID] > m_highestNumberOfFullyActivatedSlots[LAST_CLASS])
												m_highestNumberOfFullyActivatedSlots[LAST_CLASS] = m_highestNumberOfFullyActivatedSlots[classID];
											spentBytes += lastSpentBytes;
											spentOverhead += socketSentBytes.sentBytesControlPackets;
										}
									}
								}
							}
						}
					}
					if (classID < LAST_CLASS)
						realBytesToSpendClass[classID] -= spentBytes*1000;
											
					lastclientpos += slotCounterClass[classID];
					realBytesToSpendClass[LAST_CLASS] -= spentBytes*1000;
					
					m_SentBytesSinceLastCall += spentBytes;
					m_SentBytesSinceLastCallOverhead += spentOverhead;

			
				}
				if (timeSinceLastLoop > 0) {
					if (realBytesToSpendClass[LAST_CLASS] > 999 && realBytesToSpendClass[classID] > 999) {
						uint32 marge = 999;//100*ClientDataRate[classID];
						if (marge < 999)
							marge = 999;
						if (m_SentBytesSinceLastCall > 0 && realBytesToSpendClass[classID] > marge && m_highestNumberOfFullyActivatedSlots[classID] <  lastclientpos+1 || slotCounterClass[classID] == 0)
							m_highestNumberOfFullyActivatedSlots[classID] = lastclientpos+1;
						realBytesToSpendClass[classID] = 999;
					}
				}
			}
			sendLocker.Unlock();
		}
	}

    threadEndedEvent->SetEvent();

    tempQueueLocker.Lock();
    m_TempControlQueue_list.RemoveAll();
	m_TempControlQueueFirst_list.RemoveAll();
    tempQueueLocker.Unlock();

    sendLocker.Lock();

    m_ControlQueue_list.RemoveAll();
    POSITION			pos = m_stat_list.GetStartPosition();
	ThrottledFileSocket* socket;
	Socket_stat* stat;
	while (pos)
	{
		m_stat_list.GetNextAssoc(pos, socket, stat);
		delete stat;
	}
	m_stat_list.RemoveAll();
	m_StandardOrder_list.RemoveAll();
    sendLocker.Unlock();

    return 0;
}
